mirror of
https://github.com/signalapp/Signal-Desktop.git
synced 2026-08-13 08:22:26 +01:00
Adopt libsignal getDevices and setDeviceName APIs
Co-authored-by: trevor-signal <131492920+trevor-signal@users.noreply.github.com>
This commit is contained in:
co-authored by
trevor-signal
parent
ddaba5d290
commit
9ba6b515bd
@@ -9,6 +9,7 @@ import {
|
||||
BackupKey,
|
||||
} from '@signalapp/libsignal-client/dist/AccountKeys.js';
|
||||
import type { RegisterAccountResponse } from '@signalapp/libsignal-client/dist/net';
|
||||
import { Base64 } from '@signalapp/types';
|
||||
|
||||
import EventTarget from './EventTarget.std.ts';
|
||||
import {
|
||||
@@ -295,7 +296,7 @@ export default class AccountManager extends EventTarget {
|
||||
encryptDeviceName(
|
||||
name: string,
|
||||
identityKey: KeyPairType
|
||||
): string | undefined {
|
||||
): Base64 | undefined {
|
||||
if (!name) {
|
||||
return undefined;
|
||||
}
|
||||
@@ -306,17 +307,17 @@ export default class AccountManager extends EventTarget {
|
||||
syntheticIv: encrypted.syntheticIv,
|
||||
ciphertext: encrypted.ciphertext,
|
||||
});
|
||||
return Bytes.toBase64(bytes);
|
||||
return Base64.fromBytes(bytes);
|
||||
}
|
||||
|
||||
async decryptDeviceName(base64: string): Promise<string> {
|
||||
async decryptDeviceName(base64: Base64): Promise<string> {
|
||||
const ourAci = itemStorage.user.getCheckedAci();
|
||||
const identityKey = signalProtocolStore.getIdentityKeyPair(ourAci);
|
||||
if (!identityKey) {
|
||||
throw new Error('decryptDeviceName: No identity key pair!');
|
||||
}
|
||||
|
||||
const bytes = Bytes.fromBase64(base64);
|
||||
const bytes = Base64.toBytes(base64);
|
||||
const proto = Proto.DeviceName.decode(bytes);
|
||||
strictAssert(
|
||||
proto.ephemeralPublic,
|
||||
@@ -341,7 +342,7 @@ export default class AccountManager extends EventTarget {
|
||||
async _encryptDeviceCreatedAt(
|
||||
createdAt: number,
|
||||
deviceId: number
|
||||
): Promise<string> {
|
||||
): Promise<Base64> {
|
||||
const ourAci = itemStorage.user.getCheckedAci();
|
||||
const identityKey = signalProtocolStore.getIdentityKeyPair(ourAci);
|
||||
const registrationId =
|
||||
@@ -356,11 +357,11 @@ export default class AccountManager extends EventTarget {
|
||||
identityKey.publicKey
|
||||
);
|
||||
|
||||
return Bytes.toBase64(createdAtCiphertextBytes);
|
||||
return Base64.fromBytes(createdAtCiphertextBytes);
|
||||
}
|
||||
|
||||
async decryptDeviceCreatedAt(
|
||||
createdAtCiphertextBase64: string,
|
||||
createdAtCiphertextBase64: Base64,
|
||||
deviceId: number
|
||||
): Promise<number> {
|
||||
const ourAci = itemStorage.user.getCheckedAci();
|
||||
@@ -405,7 +406,10 @@ export default class AccountManager extends EventTarget {
|
||||
const base64 = this.encryptDeviceName(deviceName || '', identityKeyPair);
|
||||
|
||||
if (base64) {
|
||||
await updateDeviceName(base64);
|
||||
await updateDeviceName({
|
||||
deviceId: itemStorage.user.getCheckedDeviceId(),
|
||||
encryptedName: Base64.toBytes(base64),
|
||||
});
|
||||
await itemStorage.user.setDeviceNameEncrypted();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,6 +22,7 @@ import {
|
||||
import { AccountAttributes } from '@signalapp/libsignal-client/dist/net.js';
|
||||
import type {
|
||||
BackupAuth,
|
||||
LinkedDevice,
|
||||
ProvisioningConnection,
|
||||
ProvisioningConnectionListener,
|
||||
RegisterAccountResponse,
|
||||
@@ -819,7 +820,6 @@ const CHAT_CALLS = {
|
||||
subscriptions: 'v1/subscription',
|
||||
subscriptionConfiguration: 'v1/subscription/configuration',
|
||||
transferArchive: 'v1/devices/transfer_archive',
|
||||
updateDeviceName: 'v1/accounts/name',
|
||||
username: 'v1/accounts/username_hash',
|
||||
reserveUsername: 'v1/accounts/username_hash/reserve',
|
||||
confirmUsername: 'v1/accounts/username_hash/confirm',
|
||||
@@ -1004,19 +1004,6 @@ export type GetAccountForUsernameOptionsType = Readonly<{
|
||||
|
||||
export type GetAccountForUsernameResultType = AciString | null;
|
||||
|
||||
const getDevicesResultZod = z.object({
|
||||
devices: z.array(
|
||||
z.object({
|
||||
id: z.number(),
|
||||
name: z.string().nullish(), // primary devices may not have a name
|
||||
lastSeen: z.number().nullish(),
|
||||
createdAtCiphertext: z.string(),
|
||||
})
|
||||
),
|
||||
});
|
||||
|
||||
export type GetDevicesResultType = z.infer<typeof getDevicesResultZod>;
|
||||
|
||||
export type GetIceServersResultType = Readonly<{
|
||||
relays?: ReadonlyArray<IceServerGroupType>;
|
||||
}>;
|
||||
@@ -3094,24 +3081,23 @@ export async function disableRegistrationLock(): Promise<void> {
|
||||
});
|
||||
}
|
||||
|
||||
export async function getDevices(): Promise<GetDevicesResultType> {
|
||||
return _ajax({
|
||||
host: 'chatService',
|
||||
call: 'devices',
|
||||
httpType: 'GET',
|
||||
responseType: 'json',
|
||||
zodSchema: getDevicesResultZod,
|
||||
export async function getDevices(): Promise<ReadonlyArray<LinkedDevice>> {
|
||||
return _retry(async () => {
|
||||
const chat = await socketManager.getAuthenticatedApi();
|
||||
return chat.getDevices();
|
||||
});
|
||||
}
|
||||
|
||||
export async function updateDeviceName(deviceName: string): Promise<void> {
|
||||
await _ajax({
|
||||
host: 'chatService',
|
||||
call: 'updateDeviceName',
|
||||
httpType: 'PUT',
|
||||
jsonData: {
|
||||
deviceName,
|
||||
},
|
||||
export async function updateDeviceName({
|
||||
deviceId,
|
||||
encryptedName,
|
||||
}: {
|
||||
deviceId: number;
|
||||
encryptedName: Uint8Array<ArrayBuffer>;
|
||||
}): Promise<void> {
|
||||
await _retry(async () => {
|
||||
const chat = await socketManager.getAuthenticatedApi();
|
||||
await chat.setDeviceName({ deviceId, encryptedName });
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
import PQueue from 'p-queue';
|
||||
import { Base64 } from '@signalapp/types';
|
||||
import type { DeviceNameChangeSyncEvent } from '../textsecure/messageReceiverEvents.std.ts';
|
||||
import { getDevices } from '../textsecure/WebAPI.preload.ts';
|
||||
import { MINUTE } from './durations/index.std.ts';
|
||||
@@ -57,7 +58,7 @@ export async function maybeQueueDeviceInfoFetch(): Promise<void> {
|
||||
}
|
||||
|
||||
async function fetchAndUpdateDeviceInfo() {
|
||||
const { devices } = await getDevices();
|
||||
const devices = await getDevices();
|
||||
const localDeviceId = parseIntOrThrow(
|
||||
itemStorage.user.getDeviceId(),
|
||||
'fetchAndUpdateDeviceInfo: localDeviceId'
|
||||
@@ -70,15 +71,16 @@ async function fetchAndUpdateDeviceInfo() {
|
||||
localDeviceId
|
||||
);
|
||||
|
||||
const newNameEncrypted = ourDevice.name;
|
||||
if (!newNameEncrypted) {
|
||||
if (ourDevice.encryptedName.length === 0) {
|
||||
log.error('fetchAndUpdateDeviceInfo: device had empty name');
|
||||
return;
|
||||
}
|
||||
|
||||
let newName: string;
|
||||
try {
|
||||
newName = await accountManager.decryptDeviceName(newNameEncrypted);
|
||||
newName = await accountManager.decryptDeviceName(
|
||||
Base64.fromBytes(ourDevice.encryptedName)
|
||||
);
|
||||
} catch (e) {
|
||||
const deviceNameWasEncrypted = itemStorage.user.getDeviceNameEncrypted();
|
||||
log.error(
|
||||
@@ -101,7 +103,7 @@ async function fetchAndUpdateDeviceInfo() {
|
||||
}
|
||||
|
||||
async function maybeUpdateDeviceCreatedAt(
|
||||
createdAtCiphertext: string,
|
||||
createdAtCiphertext: Uint8Array<ArrayBuffer>,
|
||||
deviceId: number
|
||||
): Promise<void> {
|
||||
const existingCreatedAt = itemStorage.user.getDeviceCreatedAt();
|
||||
@@ -109,11 +111,10 @@ async function maybeUpdateDeviceCreatedAt(
|
||||
return;
|
||||
}
|
||||
|
||||
const createdAtEncrypted = createdAtCiphertext;
|
||||
let createdAt: number | undefined;
|
||||
try {
|
||||
createdAt = await accountManager.decryptDeviceCreatedAt(
|
||||
createdAtEncrypted,
|
||||
Base64.fromBytes(createdAtCiphertext),
|
||||
deviceId
|
||||
);
|
||||
} catch (e) {
|
||||
|
||||
Reference in New Issue
Block a user