Implement megaphone conditional standard_donate with local device createdAt

This commit is contained in:
ayumi-signal
2026-01-15 09:40:22 -08:00
committed by GitHub
parent 5528cd37c0
commit 1cfda1f210
16 changed files with 452 additions and 19 deletions

View File

@@ -43,6 +43,8 @@ import {
encryptDeviceName,
generateRegistrationId,
getRandomBytes,
decryptDeviceCreatedAt,
encryptDeviceCreatedAt,
} from '../Crypto.node.js';
import {
generateKeyPair,
@@ -311,6 +313,58 @@ export default class AccountManager extends EventTarget {
return name;
}
// For testing
async _encryptDeviceCreatedAt(
createdAt: number,
deviceId: number
): Promise<string> {
const ourAci = itemStorage.user.getCheckedAci();
const identityKey = signalProtocolStore.getIdentityKeyPair(ourAci);
const registrationId =
await signalProtocolStore.getLocalRegistrationId(ourAci);
strictAssert(identityKey, 'Missing identity key pair');
strictAssert(registrationId, 'Missing registrationId for our Aci');
const createdAtCiphertextBytes = encryptDeviceCreatedAt(
createdAt,
deviceId,
registrationId,
identityKey.publicKey
);
return Bytes.toBase64(createdAtCiphertextBytes);
}
async decryptDeviceCreatedAt(
createdAtCiphertextBase64: string,
deviceId: number
): Promise<number> {
const ourAci = itemStorage.user.getCheckedAci();
const identityKey = signalProtocolStore.getIdentityKeyPair(ourAci);
if (!identityKey) {
throw new Error('decryptDeviceCreatedAt: No identity key pair!');
}
const registrationId =
await signalProtocolStore.getLocalRegistrationId(ourAci);
if (!registrationId) {
throw new Error('decryptDeviceCreatedAt: No registrationId for our Aci!');
}
const createdAtCiphertextBytes = Bytes.fromBase64(
createdAtCiphertextBase64
);
const createdAt = decryptDeviceCreatedAt(
createdAtCiphertextBytes,
deviceId,
registrationId,
identityKey.privateKey
);
return createdAt;
}
async maybeUpdateDeviceName(): Promise<void> {
const isNameEncrypted = itemStorage.user.getDeviceNameEncrypted();
if (isNameEncrypted) {

View File

@@ -967,7 +967,7 @@ const getDevicesResultZod = z.object({
id: z.number(),
name: z.string().nullish(), // primary devices may not have a name
lastSeen: z.number().nullish(),
created: z.number().nullish(),
createdAtCiphertext: z.string(),
})
),
});

View File

@@ -152,6 +152,14 @@ export class User {
return parseInt(value, 10);
}
public getDeviceCreatedAt(): number | undefined {
return this.storage.get('deviceCreatedAt');
}
public async setDeviceCreatedAt(createdAt: number): Promise<void> {
return this.storage.put('deviceCreatedAt', createdAt);
}
public getDeviceName(): string | undefined {
return this.storage.get('device_name');
}