Files
libsignal/node/ts/AccountKeys.ts
2026-03-16 18:55:17 -07:00

192 lines
5.6 KiB
TypeScript

//
// Copyright 2024 Signal Messenger, LLC.
// SPDX-License-Identifier: AGPL-3.0-only
//
/**
* Cryptographic hashing, randomness generation, etc. related to SVR/Backup Keys.
*
* Currently only the Account Entropy Pool is exposed, because no other functionality is used on Desktop.
*
* @module AccountKeys
*/
import * as crypto from 'node:crypto';
import * as Native from './Native.js';
import ByteArray from './zkgroup/internal/ByteArray.js';
import { Aci } from './Address.js';
import { PrivateKey } from './EcKeys.js';
/**
* The randomly-generated user-memorized entropy used to derive the backup key,
* with other possible future uses.
*
* Contains log_2(36^64) = ~330 bits of entropy.
*/
export class AccountEntropyPool {
/**
* Randomly generates an Account Entropy Pool and returns the canonical string
* representation of that pool.
*
* @returns cryptographically random 64 character string of characters a-z, 0-9
*/
public static generate(): string {
return Native.AccountEntropyPool_Generate();
}
/**
* Checks whether a string can be used as an account entropy pool.
*
* @returns `true` if the string is a structurally valid account entropy value.
*/
public static isValid(accountEntropyPool: string): boolean {
return Native.AccountEntropyPool_IsValid(accountEntropyPool);
}
/**
* Derives an SVR key from the given account entropy pool.
*
* `accountEntropyPool` must be a **validated** account entropy pool;
* passing an arbitrary string here is considered a programmer error.
*/
public static deriveSvrKey(
accountEntropyPool: string
): Uint8Array<ArrayBuffer> {
return Native.AccountEntropyPool_DeriveSvrKey(accountEntropyPool);
}
/**
* Derives a backup key from the given account entropy pool.
*
* `accountEntropyPool` must be a **validated** account entropy pool;
* passing an arbitrary string here is considered a programmer error.
*
* @see {@link BackupKey.generateRandom}
*/
public static deriveBackupKey(accountEntropyPool: string): BackupKey {
return new BackupKey(
Native.AccountEntropyPool_DeriveBackupKey(accountEntropyPool)
);
}
}
/**
* A key used for many aspects of backups.
*
* Clients are typically concerned with two long-lived keys: a "messages" key (sometimes called "the
* root backup key" or just "the backup key") that's derived from an {@link AccountEntropyPool}, and
* a "media" key (formally the "media root backup key") that's not derived from anything else.
*/
export class BackupKey extends ByteArray {
private readonly __type?: never;
static SIZE = 32;
constructor(contents: Uint8Array<ArrayBuffer>) {
super(contents, BackupKey.checkLength(BackupKey.SIZE));
}
/**
* Generates a random backup key.
*
* Useful for tests and for the media root backup key, which is not derived from anything else.
*
* @see {@link AccountEntropyPool.deriveBackupKey}
*/
public static generateRandom(): BackupKey {
const bytes = crypto.randomBytes(BackupKey.SIZE);
return new BackupKey(bytes);
}
/**
* Derives the backup ID to use given the current device's ACI.
*
* Used for both message and media backups.
*/
public deriveBackupId(aci: Aci): Uint8Array<ArrayBuffer> {
return Native.BackupKey_DeriveBackupId(
this.contents,
aci.getServiceIdFixedWidthBinary()
);
}
/**
* Derives the backup EC key to use given the current device's ACI.
*
* Used for both message and media backups.
*/
public deriveEcKey(aci: Aci): PrivateKey {
return PrivateKey._fromNativeHandle(
Native.BackupKey_DeriveEcKey(
this.contents,
aci.getServiceIdFixedWidthBinary()
)
);
}
/**
* Derives the AES key used for encrypted fields in local backup metadata.
*
* Only relevant for message backup keys.
*/
public deriveLocalBackupMetadataKey(): Uint8Array<ArrayBuffer> {
return Native.BackupKey_DeriveLocalBackupMetadataKey(this.contents);
}
/**
* Derives the ID for uploading media with the name `mediaName`.
*
* Only relevant for media backup keys.
*/
public deriveMediaId(mediaName: string): Uint8Array<ArrayBuffer> {
return Native.BackupKey_DeriveMediaId(this.contents, mediaName);
}
/**
* Derives the composite encryption key for re-encrypting media with the given ID.
*
* This is a concatenation of an HMAC key (32 bytes) and an AES-CBC key (also 32 bytes).
*
* Only relevant for media backup keys.
*/
public deriveMediaEncryptionKey(
mediaId: Uint8Array<ArrayBuffer>
): Uint8Array<ArrayBuffer> {
return Native.BackupKey_DeriveMediaEncryptionKey(this.contents, mediaId);
}
/**
* Derives the composite encryption key for uploading thumbnails with the given ID to the "transit
* tier" CDN.
*
* This is a concatenation of an HMAC key (32 bytes) and an AES-CBC key (also 32 bytes).
*
* Only relevant for media backup keys.
*/
public deriveThumbnailTransitEncryptionKey(
mediaId: Uint8Array<ArrayBuffer>
): Uint8Array<ArrayBuffer> {
return Native.BackupKey_DeriveThumbnailTransitEncryptionKey(
this.contents,
mediaId
);
}
}
/**
* A forward secrecy token used for deriving message backup keys.
*
* This token is retrieved from the server when restoring a backup and is used together
* with the backup key to derive the actual encryption keys for message backups.
*/
export class BackupForwardSecrecyToken extends ByteArray {
private readonly __type?: never;
static SIZE = 32;
constructor(contents: Uint8Array<ArrayBuffer>) {
super(
contents,
BackupForwardSecrecyToken.checkLength(BackupForwardSecrecyToken.SIZE)
);
}
}