mirror of
https://github.com/signalapp/libsignal.git
synced 2026-04-25 17:25:18 +02:00
192 lines
5.6 KiB
TypeScript
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)
|
|
);
|
|
}
|
|
}
|