mirror of
https://github.com/signalapp/libsignal.git
synced 2026-04-25 17:25:18 +02:00
1859 lines
48 KiB
TypeScript
1859 lines
48 KiB
TypeScript
//
|
|
// Copyright 2020-2022 Signal Messenger, LLC.
|
|
// SPDX-License-Identifier: AGPL-3.0-only
|
|
//
|
|
|
|
import { Buffer } from 'node:buffer';
|
|
|
|
import * as uuid from 'uuid';
|
|
|
|
import * as Errors from './Errors.js';
|
|
export * from './Errors.js';
|
|
|
|
import { Aci, ProtocolAddress, ServiceId } from './Address.js';
|
|
export * from './Address.js';
|
|
import { IdentityKeyPair, PrivateKey, PublicKey } from './EcKeys.js';
|
|
export * from './EcKeys.js';
|
|
import { parseUuid, Uuid } from './uuid.js';
|
|
export * from './uuid.js';
|
|
|
|
export * as usernames from './usernames.js';
|
|
|
|
export * as io from './io.js';
|
|
|
|
export * as Net from './net.js';
|
|
|
|
export * as Mp4Sanitizer from './Mp4Sanitizer.js';
|
|
export * as WebpSanitizer from './WebpSanitizer.js';
|
|
|
|
import * as Native from './Native.js';
|
|
|
|
Native.registerErrors(Errors);
|
|
|
|
// These enums must be kept in sync with their Rust counterparts.
|
|
|
|
export enum CiphertextMessageType {
|
|
Whisper = 2,
|
|
PreKey = 3,
|
|
SenderKey = 7,
|
|
Plaintext = 8,
|
|
}
|
|
|
|
export enum Direction {
|
|
Sending,
|
|
Receiving,
|
|
}
|
|
|
|
// This enum must be kept in sync with sealed_sender.proto.
|
|
export enum ContentHint {
|
|
Default = 0,
|
|
Resendable = 1,
|
|
Implicit = 2,
|
|
}
|
|
|
|
export function hkdf(
|
|
outputLength: number,
|
|
keyMaterial: Uint8Array<ArrayBuffer>,
|
|
label: Uint8Array<ArrayBuffer>,
|
|
salt: Uint8Array<ArrayBuffer> | null
|
|
): Uint8Array<ArrayBuffer> {
|
|
return Native.HKDF_DeriveSecrets(outputLength, keyMaterial, label, salt);
|
|
}
|
|
|
|
export class ScannableFingerprint {
|
|
private readonly scannable: Uint8Array<ArrayBuffer>;
|
|
|
|
private constructor(scannable: Uint8Array<ArrayBuffer>) {
|
|
this.scannable = scannable;
|
|
}
|
|
|
|
static _fromBuffer(scannable: Uint8Array<ArrayBuffer>): ScannableFingerprint {
|
|
return new ScannableFingerprint(scannable);
|
|
}
|
|
|
|
compare(other: ScannableFingerprint): boolean {
|
|
return Native.ScannableFingerprint_Compare(this.scannable, other.scannable);
|
|
}
|
|
|
|
toBuffer(): Uint8Array<ArrayBuffer> {
|
|
return this.scannable;
|
|
}
|
|
}
|
|
|
|
export class DisplayableFingerprint {
|
|
private readonly display: string;
|
|
|
|
private constructor(display: string) {
|
|
this.display = display;
|
|
}
|
|
|
|
static _fromString(display: string): DisplayableFingerprint {
|
|
return new DisplayableFingerprint(display);
|
|
}
|
|
|
|
toString(): string {
|
|
return this.display;
|
|
}
|
|
}
|
|
|
|
export class Fingerprint {
|
|
readonly _nativeHandle: Native.Fingerprint;
|
|
|
|
private constructor(nativeHandle: Native.Fingerprint) {
|
|
this._nativeHandle = nativeHandle;
|
|
}
|
|
|
|
static new(
|
|
iterations: number,
|
|
version: number,
|
|
localIdentifier: Uint8Array<ArrayBuffer>,
|
|
localKey: PublicKey,
|
|
remoteIdentifier: Uint8Array<ArrayBuffer>,
|
|
remoteKey: PublicKey
|
|
): Fingerprint {
|
|
return new Fingerprint(
|
|
Native.Fingerprint_New(
|
|
iterations,
|
|
version,
|
|
localIdentifier,
|
|
localKey,
|
|
remoteIdentifier,
|
|
remoteKey
|
|
)
|
|
);
|
|
}
|
|
|
|
public displayableFingerprint(): DisplayableFingerprint {
|
|
return DisplayableFingerprint._fromString(
|
|
Native.Fingerprint_DisplayString(this)
|
|
);
|
|
}
|
|
|
|
public scannableFingerprint(): ScannableFingerprint {
|
|
return ScannableFingerprint._fromBuffer(
|
|
Native.Fingerprint_ScannableEncoding(this)
|
|
);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Implements the <a href="https://en.wikipedia.org/wiki/AES-GCM-SIV">AES-256-GCM-SIV</a>
|
|
* authenticated stream cipher with a 12-byte nonce.
|
|
*
|
|
* AES-GCM-SIV is a multi-pass algorithm (to generate the "synthetic initialization vector"), so
|
|
* this API does not expose a streaming form.
|
|
*/
|
|
export class Aes256GcmSiv {
|
|
readonly _nativeHandle: Native.Aes256GcmSiv;
|
|
|
|
private constructor(key: Uint8Array<ArrayBuffer>) {
|
|
this._nativeHandle = Native.Aes256GcmSiv_New(key);
|
|
}
|
|
|
|
static new(key: Uint8Array<ArrayBuffer>): Aes256GcmSiv {
|
|
return new Aes256GcmSiv(key);
|
|
}
|
|
|
|
/**
|
|
* Encrypts the given plaintext using the given nonce, and authenticating the ciphertext and given
|
|
* associated data.
|
|
*
|
|
* The associated data is not included in the ciphertext; instead, it's expected to match between
|
|
* the encrypter and decrypter. If you don't need any extra data, pass an empty array.
|
|
*
|
|
* @returns The encrypted data, including an appended 16-byte authentication tag.
|
|
*/
|
|
encrypt(
|
|
message: Uint8Array<ArrayBuffer>,
|
|
nonce: Uint8Array<ArrayBuffer>,
|
|
associatedData: Uint8Array<ArrayBuffer>
|
|
): Uint8Array<ArrayBuffer> {
|
|
return Native.Aes256GcmSiv_Encrypt(this, message, nonce, associatedData);
|
|
}
|
|
|
|
/**
|
|
* Decrypts the given ciphertext using the given nonce, and authenticating the ciphertext and given
|
|
* associated data.
|
|
*
|
|
* The associated data is not included in the ciphertext; instead, it's expected to match between
|
|
* the encrypter and decrypter.
|
|
*
|
|
* @returns The decrypted data
|
|
*/
|
|
decrypt(
|
|
message: Uint8Array<ArrayBuffer>,
|
|
nonce: Uint8Array<ArrayBuffer>,
|
|
associatedData: Uint8Array<ArrayBuffer>
|
|
): Uint8Array<ArrayBuffer> {
|
|
return Native.Aes256GcmSiv_Decrypt(this, message, nonce, associatedData);
|
|
}
|
|
}
|
|
|
|
export class KEMPublicKey {
|
|
readonly _nativeHandle: Native.KyberPublicKey;
|
|
|
|
private constructor(handle: Native.KyberPublicKey) {
|
|
this._nativeHandle = handle;
|
|
}
|
|
|
|
static _fromNativeHandle(handle: Native.KyberPublicKey): KEMPublicKey {
|
|
return new KEMPublicKey(handle);
|
|
}
|
|
|
|
static deserialize(buf: Uint8Array<ArrayBuffer>): KEMPublicKey {
|
|
return new KEMPublicKey(Native.KyberPublicKey_Deserialize(buf));
|
|
}
|
|
|
|
serialize(): Uint8Array<ArrayBuffer> {
|
|
return Native.KyberPublicKey_Serialize(this);
|
|
}
|
|
}
|
|
|
|
export class KEMSecretKey {
|
|
readonly _nativeHandle: Native.KyberSecretKey;
|
|
|
|
private constructor(handle: Native.KyberSecretKey) {
|
|
this._nativeHandle = handle;
|
|
}
|
|
|
|
static _fromNativeHandle(handle: Native.KyberSecretKey): KEMSecretKey {
|
|
return new KEMSecretKey(handle);
|
|
}
|
|
|
|
static deserialize(buf: Uint8Array<ArrayBuffer>): KEMSecretKey {
|
|
return new KEMSecretKey(Native.KyberSecretKey_Deserialize(buf));
|
|
}
|
|
|
|
serialize(): Uint8Array<ArrayBuffer> {
|
|
return Native.KyberSecretKey_Serialize(this);
|
|
}
|
|
}
|
|
|
|
export class KEMKeyPair {
|
|
readonly _nativeHandle: Native.KyberKeyPair;
|
|
|
|
private constructor(handle: Native.KyberKeyPair) {
|
|
this._nativeHandle = handle;
|
|
}
|
|
|
|
static _fromNativeHandle(handle: Native.KyberKeyPair): KEMKeyPair {
|
|
return new KEMKeyPair(handle);
|
|
}
|
|
|
|
static generate(): KEMKeyPair {
|
|
return new KEMKeyPair(Native.KyberKeyPair_Generate());
|
|
}
|
|
|
|
getPublicKey(): KEMPublicKey {
|
|
return KEMPublicKey._fromNativeHandle(
|
|
Native.KyberKeyPair_GetPublicKey(this)
|
|
);
|
|
}
|
|
|
|
getSecretKey(): KEMSecretKey {
|
|
return KEMSecretKey._fromNativeHandle(
|
|
Native.KyberKeyPair_GetSecretKey(this)
|
|
);
|
|
}
|
|
}
|
|
|
|
/** The public information contained in a {@link SignedPreKeyRecord} */
|
|
export type SignedPublicPreKey = {
|
|
id: () => number;
|
|
publicKey: () => PublicKey;
|
|
signature: () => Uint8Array<ArrayBuffer>;
|
|
};
|
|
|
|
/** The public information contained in a {@link KyberPreKeyRecord} */
|
|
export type SignedKyberPublicPreKey = {
|
|
id: () => number;
|
|
publicKey: () => KEMPublicKey;
|
|
signature: () => Uint8Array<ArrayBuffer>;
|
|
};
|
|
|
|
export class PreKeyBundle {
|
|
readonly _nativeHandle: Native.PreKeyBundle;
|
|
|
|
private constructor(handle: Native.PreKeyBundle) {
|
|
this._nativeHandle = handle;
|
|
}
|
|
|
|
static new(
|
|
registration_id: number,
|
|
device_id: number,
|
|
prekey_id: number | null,
|
|
prekey: PublicKey | null,
|
|
signed_prekey_id: number,
|
|
signed_prekey: PublicKey,
|
|
signed_prekey_signature: Uint8Array<ArrayBuffer>,
|
|
identity_key: PublicKey,
|
|
kyber_prekey_id: number,
|
|
kyber_prekey: KEMPublicKey,
|
|
kyber_prekey_signature: Uint8Array<ArrayBuffer>
|
|
): PreKeyBundle {
|
|
return new PreKeyBundle(
|
|
Native.PreKeyBundle_New(
|
|
registration_id,
|
|
device_id,
|
|
prekey_id,
|
|
prekey,
|
|
signed_prekey_id,
|
|
signed_prekey,
|
|
signed_prekey_signature,
|
|
identity_key,
|
|
kyber_prekey_id,
|
|
kyber_prekey,
|
|
kyber_prekey_signature
|
|
)
|
|
);
|
|
}
|
|
|
|
deviceId(): number {
|
|
return Native.PreKeyBundle_GetDeviceId(this);
|
|
}
|
|
identityKey(): PublicKey {
|
|
return PublicKey._fromNativeHandle(
|
|
Native.PreKeyBundle_GetIdentityKey(this)
|
|
);
|
|
}
|
|
preKeyId(): number | null {
|
|
return Native.PreKeyBundle_GetPreKeyId(this);
|
|
}
|
|
preKeyPublic(): PublicKey | null {
|
|
const handle = Native.PreKeyBundle_GetPreKeyPublic(this);
|
|
|
|
if (handle == null) {
|
|
return null;
|
|
} else {
|
|
return PublicKey._fromNativeHandle(handle);
|
|
}
|
|
}
|
|
registrationId(): number {
|
|
return Native.PreKeyBundle_GetRegistrationId(this);
|
|
}
|
|
signedPreKeyId(): number {
|
|
return Native.PreKeyBundle_GetSignedPreKeyId(this);
|
|
}
|
|
signedPreKeyPublic(): PublicKey {
|
|
return PublicKey._fromNativeHandle(
|
|
Native.PreKeyBundle_GetSignedPreKeyPublic(this)
|
|
);
|
|
}
|
|
signedPreKeySignature(): Uint8Array<ArrayBuffer> {
|
|
return Native.PreKeyBundle_GetSignedPreKeySignature(this);
|
|
}
|
|
|
|
kyberPreKeyId(): number {
|
|
return Native.PreKeyBundle_GetKyberPreKeyId(this);
|
|
}
|
|
|
|
kyberPreKeyPublic(): KEMPublicKey {
|
|
return KEMPublicKey._fromNativeHandle(
|
|
Native.PreKeyBundle_GetKyberPreKeyPublic(this)
|
|
);
|
|
}
|
|
|
|
kyberPreKeySignature(): Uint8Array<ArrayBuffer> {
|
|
return Native.PreKeyBundle_GetKyberPreKeySignature(this);
|
|
}
|
|
}
|
|
|
|
export class PreKeyRecord {
|
|
readonly _nativeHandle: Native.PreKeyRecord;
|
|
|
|
private constructor(handle: Native.PreKeyRecord) {
|
|
this._nativeHandle = handle;
|
|
}
|
|
|
|
static _fromNativeHandle(nativeHandle: Native.PreKeyRecord): PreKeyRecord {
|
|
return new PreKeyRecord(nativeHandle);
|
|
}
|
|
|
|
static new(id: number, pubKey: PublicKey, privKey: PrivateKey): PreKeyRecord {
|
|
return new PreKeyRecord(Native.PreKeyRecord_New(id, pubKey, privKey));
|
|
}
|
|
|
|
static deserialize(buffer: Uint8Array<ArrayBuffer>): PreKeyRecord {
|
|
return new PreKeyRecord(Native.PreKeyRecord_Deserialize(buffer));
|
|
}
|
|
|
|
id(): number {
|
|
return Native.PreKeyRecord_GetId(this);
|
|
}
|
|
|
|
privateKey(): PrivateKey {
|
|
return PrivateKey._fromNativeHandle(
|
|
Native.PreKeyRecord_GetPrivateKey(this)
|
|
);
|
|
}
|
|
|
|
publicKey(): PublicKey {
|
|
return PublicKey._fromNativeHandle(Native.PreKeyRecord_GetPublicKey(this));
|
|
}
|
|
|
|
serialize(): Uint8Array<ArrayBuffer> {
|
|
return Native.PreKeyRecord_Serialize(this);
|
|
}
|
|
}
|
|
|
|
export class SignedPreKeyRecord implements SignedPublicPreKey {
|
|
readonly _nativeHandle: Native.SignedPreKeyRecord;
|
|
|
|
private constructor(handle: Native.SignedPreKeyRecord) {
|
|
this._nativeHandle = handle;
|
|
}
|
|
|
|
static _fromNativeHandle(
|
|
nativeHandle: Native.SignedPreKeyRecord
|
|
): SignedPreKeyRecord {
|
|
return new SignedPreKeyRecord(nativeHandle);
|
|
}
|
|
|
|
static new(
|
|
id: number,
|
|
timestamp: number,
|
|
pubKey: PublicKey,
|
|
privKey: PrivateKey,
|
|
signature: Uint8Array<ArrayBuffer>
|
|
): SignedPreKeyRecord {
|
|
return new SignedPreKeyRecord(
|
|
Native.SignedPreKeyRecord_New(id, timestamp, pubKey, privKey, signature)
|
|
);
|
|
}
|
|
|
|
static deserialize(buffer: Uint8Array<ArrayBuffer>): SignedPreKeyRecord {
|
|
return new SignedPreKeyRecord(
|
|
Native.SignedPreKeyRecord_Deserialize(buffer)
|
|
);
|
|
}
|
|
|
|
id(): number {
|
|
return Native.SignedPreKeyRecord_GetId(this);
|
|
}
|
|
|
|
privateKey(): PrivateKey {
|
|
return PrivateKey._fromNativeHandle(
|
|
Native.SignedPreKeyRecord_GetPrivateKey(this)
|
|
);
|
|
}
|
|
|
|
publicKey(): PublicKey {
|
|
return PublicKey._fromNativeHandle(
|
|
Native.SignedPreKeyRecord_GetPublicKey(this)
|
|
);
|
|
}
|
|
|
|
serialize(): Uint8Array<ArrayBuffer> {
|
|
return Native.SignedPreKeyRecord_Serialize(this);
|
|
}
|
|
|
|
signature(): Uint8Array<ArrayBuffer> {
|
|
return Native.SignedPreKeyRecord_GetSignature(this);
|
|
}
|
|
|
|
timestamp(): number {
|
|
return Native.SignedPreKeyRecord_GetTimestamp(this);
|
|
}
|
|
}
|
|
|
|
export class KyberPreKeyRecord implements SignedKyberPublicPreKey {
|
|
readonly _nativeHandle: Native.KyberPreKeyRecord;
|
|
|
|
private constructor(handle: Native.KyberPreKeyRecord) {
|
|
this._nativeHandle = handle;
|
|
}
|
|
|
|
static _fromNativeHandle(
|
|
nativeHandle: Native.KyberPreKeyRecord
|
|
): KyberPreKeyRecord {
|
|
return new KyberPreKeyRecord(nativeHandle);
|
|
}
|
|
|
|
static new(
|
|
id: number,
|
|
timestamp: number,
|
|
keyPair: KEMKeyPair,
|
|
signature: Uint8Array<ArrayBuffer>
|
|
): KyberPreKeyRecord {
|
|
return new KyberPreKeyRecord(
|
|
Native.KyberPreKeyRecord_New(id, timestamp, keyPair, signature)
|
|
);
|
|
}
|
|
|
|
serialize(): Uint8Array<ArrayBuffer> {
|
|
return Native.KyberPreKeyRecord_Serialize(this);
|
|
}
|
|
|
|
static deserialize(buffer: Uint8Array<ArrayBuffer>): KyberPreKeyRecord {
|
|
return new KyberPreKeyRecord(Native.KyberPreKeyRecord_Deserialize(buffer));
|
|
}
|
|
|
|
id(): number {
|
|
return Native.KyberPreKeyRecord_GetId(this);
|
|
}
|
|
|
|
keyPair(): KEMKeyPair {
|
|
return KEMKeyPair._fromNativeHandle(
|
|
Native.KyberPreKeyRecord_GetKeyPair(this)
|
|
);
|
|
}
|
|
|
|
publicKey(): KEMPublicKey {
|
|
return KEMPublicKey._fromNativeHandle(
|
|
Native.KyberPreKeyRecord_GetPublicKey(this)
|
|
);
|
|
}
|
|
|
|
secretKey(): KEMSecretKey {
|
|
return KEMSecretKey._fromNativeHandle(
|
|
Native.KyberPreKeyRecord_GetSecretKey(this)
|
|
);
|
|
}
|
|
|
|
signature(): Uint8Array<ArrayBuffer> {
|
|
return Native.KyberPreKeyRecord_GetSignature(this);
|
|
}
|
|
|
|
timestamp(): number {
|
|
return Native.KyberPreKeyRecord_GetTimestamp(this);
|
|
}
|
|
}
|
|
|
|
export class SignalMessage {
|
|
readonly _nativeHandle: Native.SignalMessage;
|
|
|
|
private constructor(handle: Native.SignalMessage) {
|
|
this._nativeHandle = handle;
|
|
}
|
|
|
|
static _new(
|
|
messageVersion: number,
|
|
macKey: Uint8Array<ArrayBuffer>,
|
|
senderRatchetKey: PublicKey,
|
|
counter: number,
|
|
previousCounter: number,
|
|
ciphertext: Uint8Array<ArrayBuffer>,
|
|
senderIdentityKey: PublicKey,
|
|
receiverIdentityKey: PublicKey,
|
|
pqRatchet: Uint8Array<ArrayBuffer>
|
|
): SignalMessage {
|
|
return new SignalMessage(
|
|
Native.SignalMessage_New(
|
|
messageVersion,
|
|
macKey,
|
|
senderRatchetKey,
|
|
counter,
|
|
previousCounter,
|
|
ciphertext,
|
|
senderIdentityKey,
|
|
receiverIdentityKey,
|
|
pqRatchet
|
|
)
|
|
);
|
|
}
|
|
|
|
static deserialize(buffer: Uint8Array<ArrayBuffer>): SignalMessage {
|
|
return new SignalMessage(Native.SignalMessage_Deserialize(buffer));
|
|
}
|
|
|
|
body(): Uint8Array<ArrayBuffer> {
|
|
return Native.SignalMessage_GetBody(this);
|
|
}
|
|
|
|
pqRatchet(): Uint8Array<ArrayBuffer> {
|
|
return Native.SignalMessage_GetPqRatchet(this);
|
|
}
|
|
|
|
counter(): number {
|
|
return Native.SignalMessage_GetCounter(this);
|
|
}
|
|
|
|
messageVersion(): number {
|
|
return Native.SignalMessage_GetMessageVersion(this);
|
|
}
|
|
|
|
serialize(): Uint8Array<ArrayBuffer> {
|
|
return Native.SignalMessage_GetSerialized(this);
|
|
}
|
|
|
|
verifyMac(
|
|
senderIdentityKey: PublicKey,
|
|
recevierIdentityKey: PublicKey,
|
|
macKey: Uint8Array<ArrayBuffer>
|
|
): boolean {
|
|
return Native.SignalMessage_VerifyMac(
|
|
this,
|
|
senderIdentityKey,
|
|
recevierIdentityKey,
|
|
macKey
|
|
);
|
|
}
|
|
}
|
|
|
|
export class PreKeySignalMessage {
|
|
readonly _nativeHandle: Native.PreKeySignalMessage;
|
|
|
|
private constructor(handle: Native.PreKeySignalMessage) {
|
|
this._nativeHandle = handle;
|
|
}
|
|
|
|
static _new(
|
|
messageVersion: number,
|
|
registrationId: number,
|
|
preKeyId: number | null,
|
|
signedPreKeyId: number,
|
|
baseKey: PublicKey,
|
|
identityKey: PublicKey,
|
|
signalMessage: SignalMessage
|
|
): PreKeySignalMessage {
|
|
return new PreKeySignalMessage(
|
|
Native.PreKeySignalMessage_New(
|
|
messageVersion,
|
|
registrationId,
|
|
preKeyId,
|
|
signedPreKeyId,
|
|
baseKey,
|
|
identityKey,
|
|
signalMessage
|
|
)
|
|
);
|
|
}
|
|
|
|
static deserialize(buffer: Uint8Array<ArrayBuffer>): PreKeySignalMessage {
|
|
return new PreKeySignalMessage(
|
|
Native.PreKeySignalMessage_Deserialize(buffer)
|
|
);
|
|
}
|
|
|
|
preKeyId(): number | null {
|
|
return Native.PreKeySignalMessage_GetPreKeyId(this);
|
|
}
|
|
|
|
registrationId(): number {
|
|
return Native.PreKeySignalMessage_GetRegistrationId(this);
|
|
}
|
|
|
|
signedPreKeyId(): number {
|
|
return Native.PreKeySignalMessage_GetSignedPreKeyId(this);
|
|
}
|
|
|
|
version(): number {
|
|
return Native.PreKeySignalMessage_GetVersion(this);
|
|
}
|
|
|
|
serialize(): Uint8Array<ArrayBuffer> {
|
|
return Native.PreKeySignalMessage_Serialize(this);
|
|
}
|
|
}
|
|
|
|
export class SessionRecord {
|
|
readonly _nativeHandle: Native.SessionRecord;
|
|
|
|
private constructor(nativeHandle: Native.SessionRecord) {
|
|
this._nativeHandle = nativeHandle;
|
|
}
|
|
|
|
static _fromNativeHandle(nativeHandle: Native.SessionRecord): SessionRecord {
|
|
return new SessionRecord(nativeHandle);
|
|
}
|
|
|
|
static deserialize(buffer: Uint8Array<ArrayBuffer>): SessionRecord {
|
|
return new SessionRecord(Native.SessionRecord_Deserialize(buffer));
|
|
}
|
|
|
|
serialize(): Uint8Array<ArrayBuffer> {
|
|
return Native.SessionRecord_Serialize(this);
|
|
}
|
|
|
|
archiveCurrentState(): void {
|
|
Native.SessionRecord_ArchiveCurrentState(this);
|
|
}
|
|
|
|
localRegistrationId(): number {
|
|
return Native.SessionRecord_GetLocalRegistrationId(this);
|
|
}
|
|
|
|
remoteRegistrationId(): number {
|
|
return Native.SessionRecord_GetRemoteRegistrationId(this);
|
|
}
|
|
|
|
/**
|
|
* Returns whether the current session can be used to send messages.
|
|
*
|
|
* If there is no current session, returns false.
|
|
*/
|
|
hasCurrentState(now: Date = new Date()): boolean {
|
|
return Native.SessionRecord_HasUsableSenderChain(this, now.getTime());
|
|
}
|
|
|
|
currentRatchetKeyMatches(key: PublicKey): boolean {
|
|
return Native.SessionRecord_CurrentRatchetKeyMatches(this, key);
|
|
}
|
|
}
|
|
|
|
export class ServerCertificate {
|
|
readonly _nativeHandle: Native.ServerCertificate;
|
|
|
|
static _fromNativeHandle(
|
|
nativeHandle: Native.ServerCertificate
|
|
): ServerCertificate {
|
|
return new ServerCertificate(nativeHandle);
|
|
}
|
|
|
|
private constructor(nativeHandle: Native.ServerCertificate) {
|
|
this._nativeHandle = nativeHandle;
|
|
}
|
|
|
|
static new(
|
|
keyId: number,
|
|
serverKey: PublicKey,
|
|
trustRoot: PrivateKey
|
|
): ServerCertificate {
|
|
return new ServerCertificate(
|
|
Native.ServerCertificate_New(keyId, serverKey, trustRoot)
|
|
);
|
|
}
|
|
|
|
static deserialize(buffer: Uint8Array<ArrayBuffer>): ServerCertificate {
|
|
return new ServerCertificate(Native.ServerCertificate_Deserialize(buffer));
|
|
}
|
|
|
|
certificateData(): Uint8Array<ArrayBuffer> {
|
|
return Native.ServerCertificate_GetCertificate(this);
|
|
}
|
|
|
|
key(): PublicKey {
|
|
return PublicKey._fromNativeHandle(Native.ServerCertificate_GetKey(this));
|
|
}
|
|
|
|
keyId(): number {
|
|
return Native.ServerCertificate_GetKeyId(this);
|
|
}
|
|
|
|
serialize(): Uint8Array<ArrayBuffer> {
|
|
return Native.ServerCertificate_GetSerialized(this);
|
|
}
|
|
|
|
signature(): Uint8Array<ArrayBuffer> {
|
|
return Native.ServerCertificate_GetSignature(this);
|
|
}
|
|
}
|
|
|
|
export class SenderKeyRecord {
|
|
readonly _nativeHandle: Native.SenderKeyRecord;
|
|
|
|
static _fromNativeHandle(
|
|
nativeHandle: Native.SenderKeyRecord
|
|
): SenderKeyRecord {
|
|
return new SenderKeyRecord(nativeHandle);
|
|
}
|
|
|
|
private constructor(nativeHandle: Native.SenderKeyRecord) {
|
|
this._nativeHandle = nativeHandle;
|
|
}
|
|
|
|
static deserialize(buffer: Uint8Array<ArrayBuffer>): SenderKeyRecord {
|
|
return new SenderKeyRecord(Native.SenderKeyRecord_Deserialize(buffer));
|
|
}
|
|
|
|
serialize(): Uint8Array<ArrayBuffer> {
|
|
return Native.SenderKeyRecord_Serialize(this);
|
|
}
|
|
}
|
|
|
|
export class SenderCertificate {
|
|
readonly _nativeHandle: Native.SenderCertificate;
|
|
|
|
private constructor(nativeHandle: Native.SenderCertificate) {
|
|
this._nativeHandle = nativeHandle;
|
|
}
|
|
|
|
static _fromNativeHandle(
|
|
nativeHandle: Native.SenderCertificate
|
|
): SenderCertificate {
|
|
return new SenderCertificate(nativeHandle);
|
|
}
|
|
|
|
static new(
|
|
senderUuid: string | Aci,
|
|
senderE164: string | null,
|
|
senderDeviceId: number,
|
|
senderKey: PublicKey,
|
|
expiration: number,
|
|
signerCert: ServerCertificate,
|
|
signerKey: PrivateKey
|
|
): SenderCertificate {
|
|
if (typeof senderUuid !== 'string') {
|
|
senderUuid = senderUuid.getServiceIdString();
|
|
}
|
|
return new SenderCertificate(
|
|
Native.SenderCertificate_New(
|
|
senderUuid,
|
|
senderE164,
|
|
senderDeviceId,
|
|
senderKey,
|
|
expiration,
|
|
signerCert,
|
|
signerKey
|
|
)
|
|
);
|
|
}
|
|
|
|
static deserialize(buffer: Uint8Array<ArrayBuffer>): SenderCertificate {
|
|
return new SenderCertificate(Native.SenderCertificate_Deserialize(buffer));
|
|
}
|
|
|
|
serialize(): Uint8Array<ArrayBuffer> {
|
|
return Native.SenderCertificate_GetSerialized(this);
|
|
}
|
|
|
|
certificate(): Uint8Array<ArrayBuffer> {
|
|
return Native.SenderCertificate_GetCertificate(this);
|
|
}
|
|
expiration(): number {
|
|
return Native.SenderCertificate_GetExpiration(this);
|
|
}
|
|
key(): PublicKey {
|
|
return PublicKey._fromNativeHandle(Native.SenderCertificate_GetKey(this));
|
|
}
|
|
senderE164(): string | null {
|
|
return Native.SenderCertificate_GetSenderE164(this);
|
|
}
|
|
senderUuid(): string {
|
|
return Native.SenderCertificate_GetSenderUuid(this);
|
|
}
|
|
/**
|
|
* Returns an ACI if the sender is a valid UUID, `null` otherwise.
|
|
*
|
|
* In a future release SenderCertificate will *only* support ACIs.
|
|
*/
|
|
senderAci(): Aci | null {
|
|
try {
|
|
return Aci.parseFromServiceIdString(this.senderUuid());
|
|
} catch {
|
|
return null;
|
|
}
|
|
}
|
|
senderDeviceId(): number {
|
|
return Native.SenderCertificate_GetDeviceId(this);
|
|
}
|
|
serverCertificate(): ServerCertificate {
|
|
return ServerCertificate._fromNativeHandle(
|
|
Native.SenderCertificate_GetServerCertificate(this)
|
|
);
|
|
}
|
|
signature(): Uint8Array<ArrayBuffer> {
|
|
return Native.SenderCertificate_GetSignature(this);
|
|
}
|
|
|
|
/**
|
|
* Validates `this` against the given trust root at the given current time.
|
|
*
|
|
* @see validateWithTrustRoots
|
|
*/
|
|
validate(trustRoot: PublicKey, time: number): boolean {
|
|
return Native.SenderCertificate_Validate(this, [trustRoot], time);
|
|
}
|
|
|
|
/**
|
|
* Validates `this` against the given trust roots at the given current time.
|
|
*
|
|
* Checks the certificate against each key in `trustRoots` in constant time (that is, no result
|
|
* is produced until every key is checked), making sure **one** of them has signed its embedded
|
|
* server certificate. The `time` parameter is compared numerically against ``expiration``, and
|
|
* is not required to use any specific units, but Signal uses milliseconds since 1970.
|
|
*/
|
|
validateWithTrustRoots(trustRoots: PublicKey[], time: number): boolean {
|
|
return Native.SenderCertificate_Validate(this, trustRoots, time);
|
|
}
|
|
}
|
|
|
|
function bridgeSenderKeyStore(
|
|
store: SenderKeyStore
|
|
): Native.BridgeSenderKeyStore {
|
|
return {
|
|
async storeSenderKey(
|
|
sender: Native.ProtocolAddress,
|
|
distributionId: Native.Uuid,
|
|
record: Native.SenderKeyRecord
|
|
): Promise<void> {
|
|
return store.saveSenderKey(
|
|
ProtocolAddress._fromNativeHandle(sender),
|
|
uuid.stringify(distributionId),
|
|
SenderKeyRecord._fromNativeHandle(record)
|
|
);
|
|
},
|
|
async loadSenderKey(
|
|
sender: Native.ProtocolAddress,
|
|
distributionId: Native.Uuid
|
|
): Promise<Native.SenderKeyRecord | null> {
|
|
const sk = await store.getSenderKey(
|
|
ProtocolAddress._fromNativeHandle(sender),
|
|
uuid.stringify(distributionId)
|
|
);
|
|
return sk ? sk._nativeHandle : null;
|
|
},
|
|
};
|
|
}
|
|
|
|
export class SenderKeyDistributionMessage {
|
|
readonly _nativeHandle: Native.SenderKeyDistributionMessage;
|
|
|
|
private constructor(nativeHandle: Native.SenderKeyDistributionMessage) {
|
|
this._nativeHandle = nativeHandle;
|
|
}
|
|
|
|
static async create(
|
|
sender: ProtocolAddress,
|
|
distributionId: Uuid,
|
|
store: SenderKeyStore
|
|
): Promise<SenderKeyDistributionMessage> {
|
|
const handle = await Native.SenderKeyDistributionMessage_Create(
|
|
sender,
|
|
parseUuid(distributionId),
|
|
bridgeSenderKeyStore(store)
|
|
);
|
|
return new SenderKeyDistributionMessage(handle);
|
|
}
|
|
|
|
static _new(
|
|
messageVersion: number,
|
|
distributionId: Uuid,
|
|
chainId: number,
|
|
iteration: number,
|
|
chainKey: Uint8Array<ArrayBuffer>,
|
|
pk: PublicKey
|
|
): SenderKeyDistributionMessage {
|
|
return new SenderKeyDistributionMessage(
|
|
Native.SenderKeyDistributionMessage_New(
|
|
messageVersion,
|
|
parseUuid(distributionId),
|
|
chainId,
|
|
iteration,
|
|
chainKey,
|
|
pk
|
|
)
|
|
);
|
|
}
|
|
|
|
static deserialize(
|
|
buffer: Uint8Array<ArrayBuffer>
|
|
): SenderKeyDistributionMessage {
|
|
return new SenderKeyDistributionMessage(
|
|
Native.SenderKeyDistributionMessage_Deserialize(buffer)
|
|
);
|
|
}
|
|
|
|
serialize(): Uint8Array<ArrayBuffer> {
|
|
return Native.SenderKeyDistributionMessage_Serialize(this);
|
|
}
|
|
|
|
chainKey(): Uint8Array<ArrayBuffer> {
|
|
return Native.SenderKeyDistributionMessage_GetChainKey(this);
|
|
}
|
|
|
|
iteration(): number {
|
|
return Native.SenderKeyDistributionMessage_GetIteration(this);
|
|
}
|
|
|
|
chainId(): number {
|
|
return Native.SenderKeyDistributionMessage_GetChainId(this);
|
|
}
|
|
|
|
distributionId(): Uuid {
|
|
return uuid.stringify(
|
|
Native.SenderKeyDistributionMessage_GetDistributionId(this)
|
|
);
|
|
}
|
|
}
|
|
|
|
export async function processSenderKeyDistributionMessage(
|
|
sender: ProtocolAddress,
|
|
message: SenderKeyDistributionMessage,
|
|
store: SenderKeyStore
|
|
): Promise<void> {
|
|
await Native.SenderKeyDistributionMessage_Process(
|
|
sender,
|
|
message,
|
|
bridgeSenderKeyStore(store)
|
|
);
|
|
}
|
|
|
|
export class SenderKeyMessage {
|
|
readonly _nativeHandle: Native.SenderKeyMessage;
|
|
|
|
private constructor(nativeHandle: Native.SenderKeyMessage) {
|
|
this._nativeHandle = nativeHandle;
|
|
}
|
|
|
|
static _new(
|
|
messageVersion: number,
|
|
distributionId: Uuid,
|
|
chainId: number,
|
|
iteration: number,
|
|
ciphertext: Uint8Array<ArrayBuffer>,
|
|
pk: PrivateKey
|
|
): SenderKeyMessage {
|
|
return new SenderKeyMessage(
|
|
Native.SenderKeyMessage_New(
|
|
messageVersion,
|
|
parseUuid(distributionId),
|
|
chainId,
|
|
iteration,
|
|
ciphertext,
|
|
pk
|
|
)
|
|
);
|
|
}
|
|
|
|
static deserialize(buffer: Uint8Array<ArrayBuffer>): SenderKeyMessage {
|
|
return new SenderKeyMessage(Native.SenderKeyMessage_Deserialize(buffer));
|
|
}
|
|
|
|
serialize(): Uint8Array<ArrayBuffer> {
|
|
return Native.SenderKeyMessage_Serialize(this);
|
|
}
|
|
|
|
ciphertext(): Uint8Array<ArrayBuffer> {
|
|
return Native.SenderKeyMessage_GetCipherText(this);
|
|
}
|
|
|
|
iteration(): number {
|
|
return Native.SenderKeyMessage_GetIteration(this);
|
|
}
|
|
|
|
chainId(): number {
|
|
return Native.SenderKeyMessage_GetChainId(this);
|
|
}
|
|
|
|
distributionId(): Uuid {
|
|
return uuid.stringify(Native.SenderKeyMessage_GetDistributionId(this));
|
|
}
|
|
|
|
verifySignature(key: PublicKey): boolean {
|
|
return Native.SenderKeyMessage_VerifySignature(this, key);
|
|
}
|
|
}
|
|
|
|
export class UnidentifiedSenderMessageContent {
|
|
readonly _nativeHandle: Native.UnidentifiedSenderMessageContent;
|
|
|
|
private constructor(nativeHandle: Native.UnidentifiedSenderMessageContent) {
|
|
this._nativeHandle = nativeHandle;
|
|
}
|
|
|
|
static _fromNativeHandle(
|
|
nativeHandle: Native.UnidentifiedSenderMessageContent
|
|
): UnidentifiedSenderMessageContent {
|
|
return new UnidentifiedSenderMessageContent(nativeHandle);
|
|
}
|
|
|
|
static new(
|
|
message: CiphertextMessage,
|
|
sender: SenderCertificate,
|
|
contentHint: number,
|
|
groupId: Uint8Array<ArrayBuffer> | null
|
|
): UnidentifiedSenderMessageContent {
|
|
return new UnidentifiedSenderMessageContent(
|
|
Native.UnidentifiedSenderMessageContent_New(
|
|
message,
|
|
sender,
|
|
contentHint,
|
|
groupId
|
|
)
|
|
);
|
|
}
|
|
|
|
static deserialize(
|
|
buffer: Uint8Array<ArrayBuffer>
|
|
): UnidentifiedSenderMessageContent {
|
|
return new UnidentifiedSenderMessageContent(
|
|
Native.UnidentifiedSenderMessageContent_Deserialize(buffer)
|
|
);
|
|
}
|
|
|
|
serialize(): Uint8Array<ArrayBuffer> {
|
|
return Native.UnidentifiedSenderMessageContent_Serialize(this);
|
|
}
|
|
|
|
contents(): Uint8Array<ArrayBuffer> {
|
|
return Native.UnidentifiedSenderMessageContent_GetContents(this);
|
|
}
|
|
|
|
msgType(): number {
|
|
return Native.UnidentifiedSenderMessageContent_GetMsgType(this);
|
|
}
|
|
|
|
senderCertificate(): SenderCertificate {
|
|
return SenderCertificate._fromNativeHandle(
|
|
Native.UnidentifiedSenderMessageContent_GetSenderCert(this)
|
|
);
|
|
}
|
|
|
|
contentHint(): number {
|
|
return Native.UnidentifiedSenderMessageContent_GetContentHint(this);
|
|
}
|
|
|
|
groupId(): Uint8Array<ArrayBuffer> | null {
|
|
return Native.UnidentifiedSenderMessageContent_GetGroupId(this);
|
|
}
|
|
}
|
|
|
|
export abstract class SessionStore {
|
|
abstract saveSession(
|
|
name: ProtocolAddress,
|
|
record: SessionRecord
|
|
): Promise<void>;
|
|
abstract getSession(name: ProtocolAddress): Promise<SessionRecord | null>;
|
|
abstract getExistingSessions(
|
|
addresses: ProtocolAddress[]
|
|
): Promise<SessionRecord[]>;
|
|
}
|
|
|
|
export enum IdentityChange {
|
|
// This must be kept in sync with the Rust enum of the same name.
|
|
NewOrUnchanged = 0,
|
|
ReplacedExisting = 1,
|
|
}
|
|
|
|
export abstract class IdentityKeyStore {
|
|
abstract getIdentityKey(): Promise<PrivateKey>;
|
|
async getIdentityKeyPair(): Promise<IdentityKeyPair> {
|
|
const privKey = await this.getIdentityKey();
|
|
return new IdentityKeyPair(privKey.getPublicKey(), privKey);
|
|
}
|
|
abstract getLocalRegistrationId(): Promise<number>;
|
|
abstract saveIdentity(
|
|
name: ProtocolAddress,
|
|
key: PublicKey
|
|
): Promise<IdentityChange>;
|
|
abstract isTrustedIdentity(
|
|
name: ProtocolAddress,
|
|
key: PublicKey,
|
|
direction: Direction
|
|
): Promise<boolean>;
|
|
abstract getIdentity(name: ProtocolAddress): Promise<PublicKey | null>;
|
|
}
|
|
|
|
export abstract class PreKeyStore {
|
|
abstract savePreKey(id: number, record: PreKeyRecord): Promise<void>;
|
|
abstract getPreKey(id: number): Promise<PreKeyRecord>;
|
|
abstract removePreKey(id: number): Promise<void>;
|
|
}
|
|
|
|
export abstract class SignedPreKeyStore {
|
|
abstract saveSignedPreKey(
|
|
id: number,
|
|
record: SignedPreKeyRecord
|
|
): Promise<void>;
|
|
abstract getSignedPreKey(id: number): Promise<SignedPreKeyRecord>;
|
|
}
|
|
|
|
export abstract class KyberPreKeyStore {
|
|
abstract saveKyberPreKey(
|
|
kyberPreKeyId: number,
|
|
record: KyberPreKeyRecord
|
|
): Promise<void>;
|
|
abstract getKyberPreKey(kyberPreKeyId: number): Promise<KyberPreKeyRecord>;
|
|
abstract markKyberPreKeyUsed(
|
|
kyberPreKeyId: number,
|
|
signedPreKeyId: number,
|
|
baseKey: PublicKey
|
|
): Promise<void>;
|
|
}
|
|
|
|
export abstract class SenderKeyStore {
|
|
abstract saveSenderKey(
|
|
sender: ProtocolAddress,
|
|
distributionId: Uuid,
|
|
record: SenderKeyRecord
|
|
): Promise<void>;
|
|
abstract getSenderKey(
|
|
sender: ProtocolAddress,
|
|
distributionId: Uuid
|
|
): Promise<SenderKeyRecord | null>;
|
|
}
|
|
|
|
export async function groupEncrypt(
|
|
sender: ProtocolAddress,
|
|
distributionId: Uuid,
|
|
store: SenderKeyStore,
|
|
message: Uint8Array<ArrayBuffer>
|
|
): Promise<CiphertextMessage> {
|
|
return CiphertextMessage._fromNativeHandle(
|
|
await Native.GroupCipher_EncryptMessage(
|
|
sender,
|
|
parseUuid(distributionId),
|
|
message,
|
|
bridgeSenderKeyStore(store)
|
|
)
|
|
);
|
|
}
|
|
|
|
export async function groupDecrypt(
|
|
sender: ProtocolAddress,
|
|
store: SenderKeyStore,
|
|
message: Uint8Array<ArrayBuffer>
|
|
): Promise<Uint8Array<ArrayBuffer>> {
|
|
return Native.GroupCipher_DecryptMessage(
|
|
sender,
|
|
message,
|
|
bridgeSenderKeyStore(store)
|
|
);
|
|
}
|
|
|
|
export class SealedSenderDecryptionResult {
|
|
readonly _nativeHandle: Native.SealedSenderDecryptionResult;
|
|
|
|
private constructor(nativeHandle: Native.SealedSenderDecryptionResult) {
|
|
this._nativeHandle = nativeHandle;
|
|
}
|
|
|
|
static _fromNativeHandle(
|
|
nativeHandle: Native.SealedSenderDecryptionResult
|
|
): SealedSenderDecryptionResult {
|
|
return new SealedSenderDecryptionResult(nativeHandle);
|
|
}
|
|
|
|
message(): Uint8Array<ArrayBuffer> {
|
|
return Native.SealedSenderDecryptionResult_Message(this);
|
|
}
|
|
|
|
senderE164(): string | null {
|
|
return Native.SealedSenderDecryptionResult_GetSenderE164(this);
|
|
}
|
|
|
|
senderUuid(): string {
|
|
return Native.SealedSenderDecryptionResult_GetSenderUuid(this);
|
|
}
|
|
|
|
/**
|
|
* Returns an ACI if the sender is a valid UUID, `null` otherwise.
|
|
*
|
|
* In a future release SenderCertificate will *only* support ACIs.
|
|
*/
|
|
senderAci(): Aci | null {
|
|
try {
|
|
return Aci.parseFromServiceIdString(this.senderUuid());
|
|
} catch {
|
|
return null;
|
|
}
|
|
}
|
|
|
|
deviceId(): number {
|
|
return Native.SealedSenderDecryptionResult_GetDeviceId(this);
|
|
}
|
|
}
|
|
|
|
export interface CiphertextMessageConvertible {
|
|
asCiphertextMessage: () => CiphertextMessage;
|
|
}
|
|
|
|
export class CiphertextMessage {
|
|
readonly _nativeHandle: Native.CiphertextMessage;
|
|
|
|
private constructor(nativeHandle: Native.CiphertextMessage) {
|
|
this._nativeHandle = nativeHandle;
|
|
}
|
|
|
|
static _fromNativeHandle(
|
|
nativeHandle: Native.CiphertextMessage
|
|
): CiphertextMessage {
|
|
return new CiphertextMessage(nativeHandle);
|
|
}
|
|
|
|
static from(message: CiphertextMessageConvertible): CiphertextMessage {
|
|
return message.asCiphertextMessage();
|
|
}
|
|
|
|
serialize(): Uint8Array<ArrayBuffer> {
|
|
return Native.CiphertextMessage_Serialize(this);
|
|
}
|
|
|
|
type(): number {
|
|
return Native.CiphertextMessage_Type(this);
|
|
}
|
|
}
|
|
|
|
export class PlaintextContent implements CiphertextMessageConvertible {
|
|
readonly _nativeHandle: Native.PlaintextContent;
|
|
|
|
private constructor(nativeHandle: Native.PlaintextContent) {
|
|
this._nativeHandle = nativeHandle;
|
|
}
|
|
|
|
static deserialize(buffer: Uint8Array<ArrayBuffer>): PlaintextContent {
|
|
return new PlaintextContent(Native.PlaintextContent_Deserialize(buffer));
|
|
}
|
|
|
|
static from(message: DecryptionErrorMessage): PlaintextContent {
|
|
return new PlaintextContent(
|
|
Native.PlaintextContent_FromDecryptionErrorMessage(message)
|
|
);
|
|
}
|
|
|
|
serialize(): Uint8Array<ArrayBuffer> {
|
|
return Native.PlaintextContent_Serialize(this);
|
|
}
|
|
|
|
body(): Uint8Array<ArrayBuffer> {
|
|
return Native.PlaintextContent_GetBody(this);
|
|
}
|
|
|
|
asCiphertextMessage(): CiphertextMessage {
|
|
return CiphertextMessage._fromNativeHandle(
|
|
Native.CiphertextMessage_FromPlaintextContent(this)
|
|
);
|
|
}
|
|
}
|
|
|
|
export class DecryptionErrorMessage {
|
|
readonly _nativeHandle: Native.DecryptionErrorMessage;
|
|
|
|
private constructor(nativeHandle: Native.DecryptionErrorMessage) {
|
|
this._nativeHandle = nativeHandle;
|
|
}
|
|
|
|
static _fromNativeHandle(
|
|
nativeHandle: Native.DecryptionErrorMessage
|
|
): DecryptionErrorMessage {
|
|
return new DecryptionErrorMessage(nativeHandle);
|
|
}
|
|
|
|
static forOriginal(
|
|
bytes: Uint8Array<ArrayBuffer>,
|
|
type: CiphertextMessageType,
|
|
timestamp: number,
|
|
originalSenderDeviceId: number
|
|
): DecryptionErrorMessage {
|
|
return new DecryptionErrorMessage(
|
|
Native.DecryptionErrorMessage_ForOriginalMessage(
|
|
bytes,
|
|
type,
|
|
timestamp,
|
|
originalSenderDeviceId
|
|
)
|
|
);
|
|
}
|
|
|
|
static deserialize(buffer: Uint8Array<ArrayBuffer>): DecryptionErrorMessage {
|
|
return new DecryptionErrorMessage(
|
|
Native.DecryptionErrorMessage_Deserialize(buffer)
|
|
);
|
|
}
|
|
|
|
static extractFromSerializedBody(
|
|
buffer: Uint8Array<ArrayBuffer>
|
|
): DecryptionErrorMessage {
|
|
return new DecryptionErrorMessage(
|
|
Native.DecryptionErrorMessage_ExtractFromSerializedContent(buffer)
|
|
);
|
|
}
|
|
|
|
serialize(): Uint8Array<ArrayBuffer> {
|
|
return Native.DecryptionErrorMessage_Serialize(this);
|
|
}
|
|
|
|
timestamp(): number {
|
|
return Native.DecryptionErrorMessage_GetTimestamp(this);
|
|
}
|
|
|
|
deviceId(): number {
|
|
return Native.DecryptionErrorMessage_GetDeviceId(this);
|
|
}
|
|
|
|
ratchetKey(): PublicKey | undefined {
|
|
const keyHandle = Native.DecryptionErrorMessage_GetRatchetKey(this);
|
|
if (keyHandle) {
|
|
return PublicKey._fromNativeHandle(keyHandle);
|
|
} else {
|
|
return undefined;
|
|
}
|
|
}
|
|
}
|
|
|
|
function bridgeSessionStore(store: SessionStore): Native.BridgeSessionStore {
|
|
return {
|
|
async storeSession(
|
|
rawAddress: Native.ProtocolAddress,
|
|
record: Native.SessionRecord
|
|
): Promise<void> {
|
|
return store.saveSession(
|
|
ProtocolAddress._fromNativeHandle(rawAddress),
|
|
SessionRecord._fromNativeHandle(record)
|
|
);
|
|
},
|
|
async loadSession(
|
|
rawAddress: Native.ProtocolAddress
|
|
): Promise<Native.SessionRecord | null> {
|
|
const pk = await store.getSession(
|
|
ProtocolAddress._fromNativeHandle(rawAddress)
|
|
);
|
|
return pk ? pk._nativeHandle : null;
|
|
},
|
|
};
|
|
}
|
|
|
|
function bridgeIdentityKeyStore(
|
|
store: IdentityKeyStore
|
|
): Native.BridgeIdentityKeyStore {
|
|
return {
|
|
async getLocalIdentityKeyPair(): Promise<
|
|
[Native.PrivateKey, Native.PublicKey]
|
|
> {
|
|
const keyPair = await store.getIdentityKeyPair();
|
|
return [
|
|
keyPair.privateKey._nativeHandle,
|
|
keyPair.publicKey._nativeHandle,
|
|
];
|
|
},
|
|
async getLocalRegistrationId(): Promise<number> {
|
|
return store.getLocalRegistrationId();
|
|
},
|
|
async saveIdentityKey(
|
|
name: Native.ProtocolAddress,
|
|
key: Native.PublicKey
|
|
): Promise<Native.IdentityChange> {
|
|
return store.saveIdentity(
|
|
ProtocolAddress._fromNativeHandle(name),
|
|
PublicKey._fromNativeHandle(key)
|
|
);
|
|
},
|
|
async isTrustedIdentity(
|
|
name: Native.ProtocolAddress,
|
|
key: Native.PublicKey,
|
|
direction: number
|
|
): Promise<boolean> {
|
|
return store.isTrustedIdentity(
|
|
ProtocolAddress._fromNativeHandle(name),
|
|
PublicKey._fromNativeHandle(key),
|
|
direction as Direction
|
|
);
|
|
},
|
|
async getIdentityKey(
|
|
name: Native.ProtocolAddress
|
|
): Promise<Native.PublicKey | null> {
|
|
const key = await store.getIdentity(
|
|
ProtocolAddress._fromNativeHandle(name)
|
|
);
|
|
return key ? key._nativeHandle : null;
|
|
},
|
|
};
|
|
}
|
|
|
|
export function processPreKeyBundle(
|
|
bundle: PreKeyBundle,
|
|
address: ProtocolAddress,
|
|
sessionStore: SessionStore,
|
|
identityStore: IdentityKeyStore,
|
|
now: Date = new Date()
|
|
): Promise<void> {
|
|
return Native.SessionBuilder_ProcessPreKeyBundle(
|
|
bundle,
|
|
address,
|
|
bridgeSessionStore(sessionStore),
|
|
bridgeIdentityKeyStore(identityStore),
|
|
now.getTime()
|
|
);
|
|
}
|
|
|
|
export async function signalEncrypt(
|
|
message: Uint8Array<ArrayBuffer>,
|
|
address: ProtocolAddress,
|
|
sessionStore: SessionStore,
|
|
identityStore: IdentityKeyStore,
|
|
now: Date = new Date()
|
|
): Promise<CiphertextMessage> {
|
|
return CiphertextMessage._fromNativeHandle(
|
|
await Native.SessionCipher_EncryptMessage(
|
|
message,
|
|
address,
|
|
bridgeSessionStore(sessionStore),
|
|
bridgeIdentityKeyStore(identityStore),
|
|
now.getTime()
|
|
)
|
|
);
|
|
}
|
|
|
|
export function signalDecrypt(
|
|
message: SignalMessage,
|
|
address: ProtocolAddress,
|
|
sessionStore: SessionStore,
|
|
identityStore: IdentityKeyStore
|
|
): Promise<Uint8Array<ArrayBuffer>> {
|
|
return Native.SessionCipher_DecryptSignalMessage(
|
|
message,
|
|
address,
|
|
bridgeSessionStore(sessionStore),
|
|
bridgeIdentityKeyStore(identityStore)
|
|
);
|
|
}
|
|
|
|
function bridgePreKeyStore(store: PreKeyStore): Native.BridgePreKeyStore {
|
|
return {
|
|
async storePreKey(id: number, record: Native.PreKeyRecord): Promise<void> {
|
|
return store.savePreKey(id, PreKeyRecord._fromNativeHandle(record));
|
|
},
|
|
async loadPreKey(id: number): Promise<Native.PreKeyRecord> {
|
|
const pk = await store.getPreKey(id);
|
|
return pk._nativeHandle;
|
|
},
|
|
async removePreKey(id: number): Promise<void> {
|
|
return store.removePreKey(id);
|
|
},
|
|
};
|
|
}
|
|
|
|
function bridgeSignedPreKeyStore(
|
|
store: SignedPreKeyStore
|
|
): Native.BridgeSignedPreKeyStore {
|
|
return {
|
|
async storeSignedPreKey(
|
|
id: number,
|
|
record: Native.SignedPreKeyRecord
|
|
): Promise<void> {
|
|
return store.saveSignedPreKey(
|
|
id,
|
|
SignedPreKeyRecord._fromNativeHandle(record)
|
|
);
|
|
},
|
|
async loadSignedPreKey(id: number): Promise<Native.SignedPreKeyRecord> {
|
|
const pk = await store.getSignedPreKey(id);
|
|
return pk._nativeHandle;
|
|
},
|
|
};
|
|
}
|
|
|
|
function bridgeKyberPreKeyStore(
|
|
store: KyberPreKeyStore
|
|
): Native.BridgeKyberPreKeyStore {
|
|
return {
|
|
async storeKyberPreKey(
|
|
id: number,
|
|
record: Native.KyberPreKeyRecord
|
|
): Promise<void> {
|
|
return store.saveKyberPreKey(
|
|
id,
|
|
KyberPreKeyRecord._fromNativeHandle(record)
|
|
);
|
|
},
|
|
async loadKyberPreKey(id: number): Promise<Native.KyberPreKeyRecord> {
|
|
const pk = await store.getKyberPreKey(id);
|
|
return pk._nativeHandle;
|
|
},
|
|
async markKyberPreKeyUsed(
|
|
id: number,
|
|
ecPrekeyId: number,
|
|
baseKey: Native.PublicKey
|
|
): Promise<void> {
|
|
return store.markKyberPreKeyUsed(
|
|
id,
|
|
ecPrekeyId,
|
|
PublicKey._fromNativeHandle(baseKey)
|
|
);
|
|
},
|
|
};
|
|
}
|
|
|
|
export function signalDecryptPreKey(
|
|
message: PreKeySignalMessage,
|
|
address: ProtocolAddress,
|
|
sessionStore: SessionStore,
|
|
identityStore: IdentityKeyStore,
|
|
prekeyStore: PreKeyStore,
|
|
signedPrekeyStore: SignedPreKeyStore,
|
|
kyberPrekeyStore: KyberPreKeyStore
|
|
): Promise<Uint8Array<ArrayBuffer>> {
|
|
return Native.SessionCipher_DecryptPreKeySignalMessage(
|
|
message,
|
|
address,
|
|
bridgeSessionStore(sessionStore),
|
|
bridgeIdentityKeyStore(identityStore),
|
|
bridgePreKeyStore(prekeyStore),
|
|
bridgeSignedPreKeyStore(signedPrekeyStore),
|
|
bridgeKyberPreKeyStore(kyberPrekeyStore)
|
|
);
|
|
}
|
|
|
|
export async function sealedSenderEncryptMessage(
|
|
message: Uint8Array<ArrayBuffer>,
|
|
address: ProtocolAddress,
|
|
senderCert: SenderCertificate,
|
|
sessionStore: SessionStore,
|
|
identityStore: IdentityKeyStore
|
|
): Promise<Uint8Array<ArrayBuffer>> {
|
|
const ciphertext = await signalEncrypt(
|
|
message,
|
|
address,
|
|
sessionStore,
|
|
identityStore
|
|
);
|
|
const usmc = UnidentifiedSenderMessageContent.new(
|
|
ciphertext,
|
|
senderCert,
|
|
ContentHint.Default,
|
|
null
|
|
);
|
|
return await sealedSenderEncrypt(usmc, address, identityStore);
|
|
}
|
|
|
|
export function sealedSenderEncrypt(
|
|
content: UnidentifiedSenderMessageContent,
|
|
address: ProtocolAddress,
|
|
identityStore: IdentityKeyStore
|
|
): Promise<Uint8Array<ArrayBuffer>> {
|
|
return Native.SealedSender_Encrypt(
|
|
address,
|
|
content,
|
|
bridgeIdentityKeyStore(identityStore)
|
|
);
|
|
}
|
|
|
|
export type SealedSenderMultiRecipientEncryptOptions = {
|
|
content: UnidentifiedSenderMessageContent;
|
|
recipients: ProtocolAddress[];
|
|
excludedRecipients?: ServiceId[];
|
|
identityStore: IdentityKeyStore;
|
|
sessionStore: SessionStore;
|
|
};
|
|
|
|
export async function sealedSenderMultiRecipientEncrypt(
|
|
options: SealedSenderMultiRecipientEncryptOptions
|
|
): Promise<Uint8Array<ArrayBuffer>>;
|
|
export async function sealedSenderMultiRecipientEncrypt(
|
|
content: UnidentifiedSenderMessageContent,
|
|
recipients: ProtocolAddress[],
|
|
identityStore: IdentityKeyStore,
|
|
sessionStore: SessionStore
|
|
): Promise<Uint8Array<ArrayBuffer>>;
|
|
|
|
export async function sealedSenderMultiRecipientEncrypt(
|
|
contentOrOptions:
|
|
| UnidentifiedSenderMessageContent
|
|
| SealedSenderMultiRecipientEncryptOptions,
|
|
recipients?: ProtocolAddress[],
|
|
identityStore?: IdentityKeyStore,
|
|
sessionStore?: SessionStore
|
|
): Promise<Uint8Array<ArrayBuffer>> {
|
|
let excludedRecipients: ServiceId[] | undefined = undefined;
|
|
if (contentOrOptions instanceof UnidentifiedSenderMessageContent) {
|
|
if (!recipients || !identityStore || !sessionStore) {
|
|
throw Error('missing arguments for sealedSenderMultiRecipientEncrypt');
|
|
}
|
|
} else {
|
|
({
|
|
content: contentOrOptions,
|
|
recipients,
|
|
excludedRecipients,
|
|
identityStore,
|
|
sessionStore,
|
|
} = contentOrOptions);
|
|
}
|
|
|
|
const recipientSessions = await sessionStore.getExistingSessions(recipients);
|
|
return await Native.SealedSender_MultiRecipientEncrypt(
|
|
recipients,
|
|
recipientSessions,
|
|
ServiceId.toConcatenatedFixedWidthBinary(excludedRecipients ?? []),
|
|
contentOrOptions,
|
|
bridgeIdentityKeyStore(identityStore)
|
|
);
|
|
}
|
|
|
|
// For testing only
|
|
export function sealedSenderMultiRecipientMessageForSingleRecipient(
|
|
message: Uint8Array<ArrayBuffer>
|
|
): Uint8Array<ArrayBuffer> {
|
|
return Native.SealedSender_MultiRecipientMessageForSingleRecipient(message);
|
|
}
|
|
|
|
export async function sealedSenderDecryptMessage(
|
|
message: Uint8Array<ArrayBuffer>,
|
|
trustRoot: PublicKey,
|
|
timestamp: number,
|
|
localE164: string | null,
|
|
localUuid: string,
|
|
localDeviceId: number,
|
|
sessionStore: SessionStore,
|
|
identityStore: IdentityKeyStore,
|
|
prekeyStore: PreKeyStore,
|
|
signedPrekeyStore: SignedPreKeyStore,
|
|
kyberPrekeyStore: KyberPreKeyStore
|
|
): Promise<SealedSenderDecryptionResult> {
|
|
const ssdr = await Native.SealedSender_DecryptMessage(
|
|
message,
|
|
trustRoot,
|
|
timestamp,
|
|
localE164,
|
|
localUuid,
|
|
localDeviceId,
|
|
bridgeSessionStore(sessionStore),
|
|
bridgeIdentityKeyStore(identityStore),
|
|
bridgePreKeyStore(prekeyStore),
|
|
bridgeSignedPreKeyStore(signedPrekeyStore),
|
|
bridgeKyberPreKeyStore(kyberPrekeyStore)
|
|
);
|
|
return SealedSenderDecryptionResult._fromNativeHandle(ssdr);
|
|
}
|
|
|
|
export async function sealedSenderDecryptToUsmc(
|
|
message: Uint8Array<ArrayBuffer>,
|
|
identityStore: IdentityKeyStore
|
|
): Promise<UnidentifiedSenderMessageContent> {
|
|
const usmc = await Native.SealedSender_DecryptToUsmc(
|
|
message,
|
|
bridgeIdentityKeyStore(identityStore)
|
|
);
|
|
return UnidentifiedSenderMessageContent._fromNativeHandle(usmc);
|
|
}
|
|
|
|
export class Cds2Client {
|
|
readonly _nativeHandle: Native.SgxClientState;
|
|
|
|
private constructor(nativeHandle: Native.SgxClientState) {
|
|
this._nativeHandle = nativeHandle;
|
|
}
|
|
|
|
static new(
|
|
mrenclave: Uint8Array<ArrayBuffer>,
|
|
attestationMsg: Uint8Array<ArrayBuffer>,
|
|
currentTimestamp: Date
|
|
): Cds2Client {
|
|
return new Cds2Client(
|
|
Native.Cds2ClientState_New(
|
|
mrenclave,
|
|
attestationMsg,
|
|
currentTimestamp.getTime()
|
|
)
|
|
);
|
|
}
|
|
|
|
initialRequest(): Uint8Array<ArrayBuffer> {
|
|
return Native.SgxClientState_InitialRequest(this);
|
|
}
|
|
|
|
completeHandshake(buffer: Uint8Array<ArrayBuffer>): void {
|
|
return Native.SgxClientState_CompleteHandshake(this, buffer);
|
|
}
|
|
|
|
establishedSend(buffer: Uint8Array<ArrayBuffer>): Uint8Array<ArrayBuffer> {
|
|
return Native.SgxClientState_EstablishedSend(this, buffer);
|
|
}
|
|
|
|
establishedRecv(buffer: Uint8Array<ArrayBuffer>): Uint8Array<ArrayBuffer> {
|
|
return Native.SgxClientState_EstablishedRecv(this, buffer);
|
|
}
|
|
}
|
|
|
|
export class HsmEnclaveClient {
|
|
readonly _nativeHandle: Native.HsmEnclaveClient;
|
|
|
|
private constructor(nativeHandle: Native.HsmEnclaveClient) {
|
|
this._nativeHandle = nativeHandle;
|
|
}
|
|
|
|
static new(
|
|
public_key: Uint8Array<ArrayBuffer>,
|
|
code_hashes: Uint8Array<ArrayBuffer>[]
|
|
): HsmEnclaveClient {
|
|
code_hashes.forEach((hash) => {
|
|
if (hash.length != 32) {
|
|
throw new Error('code hash length must be 32');
|
|
}
|
|
});
|
|
const concat_hashes = Buffer.concat(code_hashes);
|
|
|
|
return new HsmEnclaveClient(
|
|
Native.HsmEnclaveClient_New(public_key, concat_hashes)
|
|
);
|
|
}
|
|
|
|
initialRequest(): Uint8Array<ArrayBuffer> {
|
|
return Native.HsmEnclaveClient_InitialRequest(this);
|
|
}
|
|
|
|
completeHandshake(buffer: Uint8Array<ArrayBuffer>): void {
|
|
return Native.HsmEnclaveClient_CompleteHandshake(this, buffer);
|
|
}
|
|
|
|
establishedSend(buffer: Uint8Array<ArrayBuffer>): Uint8Array<ArrayBuffer> {
|
|
return Native.HsmEnclaveClient_EstablishedSend(this, buffer);
|
|
}
|
|
|
|
establishedRecv(buffer: Uint8Array<ArrayBuffer>): Uint8Array<ArrayBuffer> {
|
|
return Native.HsmEnclaveClient_EstablishedRecv(this, buffer);
|
|
}
|
|
}
|
|
|
|
export enum LogLevel {
|
|
Error = 1,
|
|
Warn,
|
|
Info,
|
|
Debug,
|
|
Trace,
|
|
}
|
|
|
|
export function initLogger(
|
|
maxLevel: LogLevel,
|
|
callback: (
|
|
level: LogLevel,
|
|
target: string,
|
|
file: string | null,
|
|
line: number | null,
|
|
message: string
|
|
) => void
|
|
): void {
|
|
let nativeMaxLevel: Native.LogLevel;
|
|
switch (maxLevel) {
|
|
case LogLevel.Error:
|
|
nativeMaxLevel = Native.LogLevel.Error;
|
|
break;
|
|
case LogLevel.Warn:
|
|
nativeMaxLevel = Native.LogLevel.Warn;
|
|
break;
|
|
case LogLevel.Info:
|
|
nativeMaxLevel = Native.LogLevel.Info;
|
|
break;
|
|
case LogLevel.Debug:
|
|
nativeMaxLevel = Native.LogLevel.Debug;
|
|
break;
|
|
case LogLevel.Trace:
|
|
nativeMaxLevel = Native.LogLevel.Trace;
|
|
break;
|
|
}
|
|
Native.initLogger(
|
|
nativeMaxLevel,
|
|
(nativeLevel, target, file, line, message) => {
|
|
let level: LogLevel;
|
|
switch (nativeLevel) {
|
|
case Native.LogLevel.Error:
|
|
level = LogLevel.Error;
|
|
break;
|
|
case Native.LogLevel.Warn:
|
|
level = LogLevel.Warn;
|
|
break;
|
|
case Native.LogLevel.Info:
|
|
level = LogLevel.Info;
|
|
break;
|
|
case Native.LogLevel.Debug:
|
|
level = LogLevel.Debug;
|
|
break;
|
|
case Native.LogLevel.Trace:
|
|
level = LogLevel.Trace;
|
|
break;
|
|
default:
|
|
callback(
|
|
LogLevel.Warn,
|
|
'signal-client',
|
|
'index.ts',
|
|
0,
|
|
`unknown log level ${nativeLevel}; treating as error`
|
|
);
|
|
level = LogLevel.Error;
|
|
break;
|
|
}
|
|
callback(level, target, file, line, message);
|
|
}
|
|
);
|
|
}
|