diff --git a/js/modules/data.d.ts b/js/modules/data.d.ts index 92441555f..c70fd17e1 100644 --- a/js/modules/data.d.ts +++ b/js/modules/data.d.ts @@ -179,7 +179,7 @@ export function updateGuardNodes(nodes: Array): Promise; // Storage Items export function createOrUpdateItem(data: StorageItem): Promise; -export function getItemById(id: string): Promise; +export function getItemById(id: string): Promise; export function getAlItems(): Promise>; export function bulkAddItems(array: Array): Promise; export function removeItemById(id: string): Promise; diff --git a/js/modules/metadata/SecretSessionCipher.d.ts b/js/modules/metadata/SecretSessionCipher.d.ts new file mode 100644 index 000000000..1375167fd --- /dev/null +++ b/js/modules/metadata/SecretSessionCipher.d.ts @@ -0,0 +1,23 @@ +import { SignalService } from '../../../ts/protobuf'; +import { + BinaryString, + CipherTextObject, +} from '../../../libtextsecure/libsignal-protocol'; + +export declare class SecretSessionCipher { + constructor(storage: any); + encrypt( + destinationPubkey: string, + senderCertificate: SignalService.SenderCertificate, + innerEncryptedMessage: CipherTextObject + ): Promise; + decrypt( + cipherText: ArrayBuffer, + me: { number: string; deviceId: number } + ): Promise<{ + isMe?: boolean; + sender: string; + content: ArrayBuffer; + type: SignalService.Envelope.Type; + }>; +} diff --git a/js/modules/signal.d.ts b/js/modules/signal.d.ts new file mode 100644 index 000000000..f395f30a6 --- /dev/null +++ b/js/modules/signal.d.ts @@ -0,0 +1,9 @@ +import { SecretSessionCipher } from './metadata/SecretSessionCipher'; + +interface Metadata { + SecretSessionCipher: typeof SecretSessionCipher; +} + +export interface SignalInterface { + Metadata: Metadata; +} diff --git a/ts/session/crypto/MessageEncrypter.ts b/ts/session/crypto/MessageEncrypter.ts index d58a11f2f..f19a3bbfa 100644 --- a/ts/session/crypto/MessageEncrypter.ts +++ b/ts/session/crypto/MessageEncrypter.ts @@ -1,12 +1,10 @@ import { EncryptionType } from '../types/EncryptionType'; import { SignalService } from '../../protobuf'; -import { libloki, libsignal, textsecure } from '../../window'; -import { - CipherTextObject, - SignalProtocolAddress, -} from '../../../libtextsecure/libsignal-protocol'; +import { libloki, libsignal, Signal, textsecure } from '../../window'; +import { CipherTextObject } from '../../../libtextsecure/libsignal-protocol'; +import { UserUtil } from '../../util'; -function padPlainTextBuffer(messageBuffer: Uint8Array): Uint8Array { +export function padPlainTextBuffer(messageBuffer: Uint8Array): Uint8Array { const plaintext = new Uint8Array( getPaddedMessageLength(messageBuffer.byteLength + 1) - 1 ); @@ -53,31 +51,49 @@ export async function encrypt( throw new Error('Encryption is not yet supported'); } - let cipherText: CipherTextObject; + let innerCipherText: CipherTextObject; if (encryptionType === EncryptionType.SessionReset) { const cipher = new libloki.crypto.FallBackSessionCipher(address); - cipherText = await cipher.encrypt(plainText.buffer); + innerCipherText = await cipher.encrypt(plainText.buffer); } else { const cipher = new libsignal.SessionCipher( textsecure.storage.protocol, address ); - cipherText = await cipher.encrypt(plainText.buffer); + innerCipherText = await cipher.encrypt(plainText.buffer); } - return encryptUsingSealedSender(address, cipherText); + return encryptUsingSealedSender(device, innerCipherText); } async function encryptUsingSealedSender( - address: SignalProtocolAddress, - cipherText: CipherTextObject + device: string, + innerCipherText: CipherTextObject ): Promise<{ envelopeType: SignalService.Envelope.Type; cipherText: Base64String; }> { - // TODO: Do stuff here + const ourNumber = await UserUtil.getCurrentDevicePubKey(); + if (!ourNumber) { + throw new Error('Failed to fetch current device public key.'); + } + + const certificate = SignalService.SenderCertificate.create({ + sender: ourNumber, + senderDevice: 1, + }); + + const cipher = new Signal.Metadata.SecretSessionCipher( + textsecure.storage.protocol + ); + const cipherTextBuffer = await cipher.encrypt( + device, + certificate, + innerCipherText + ); + return { envelopeType: SignalService.Envelope.Type.UNIDENTIFIED_SENDER, - cipherText: 'implement me!', + cipherText: Buffer.from(cipherTextBuffer).toString('base64'), }; } diff --git a/ts/util/index.ts b/ts/util/index.ts index ec0ca2e21..8416f8630 100644 --- a/ts/util/index.ts +++ b/ts/util/index.ts @@ -4,6 +4,7 @@ import { isFileDangerous } from './isFileDangerous'; import { missingCaseError } from './missingCaseError'; import { migrateColor } from './migrateColor'; import { makeLookup } from './makeLookup'; +import * as UserUtil from './user'; export { arrayBufferToObjectURL, @@ -12,4 +13,5 @@ export { makeLookup, migrateColor, missingCaseError, + UserUtil, }; diff --git a/ts/util/user.ts b/ts/util/user.ts new file mode 100644 index 000000000..cea991b17 --- /dev/null +++ b/ts/util/user.ts @@ -0,0 +1,17 @@ +import { getItemById } from '../../js/modules/data'; +import { KeyPair } from '../../libtextsecure/libsignal-protocol'; + +export async function getCurrentDevicePubKey(): Promise { + const item = await getItemById('number_id'); + if (!item || !item.value) { + return undefined; + } + + return item.value.split('.')[0]; +} + +export async function getIdentityKeyPair(): Promise { + const item = await getItemById('identityKey'); + + return item?.value; +} diff --git a/ts/window.ts b/ts/window.ts index cc5a28be3..1cfa81639 100644 --- a/ts/window.ts +++ b/ts/window.ts @@ -1,5 +1,6 @@ import { LocalizerType } from './types/Util'; import { LibsignalProtocol } from '../libtextsecure/libsignal-protocol'; +import { SignalInterface } from '../js/modules/signal'; interface WindowInterface extends Window { seedNodeList: any; @@ -37,7 +38,7 @@ interface WindowInterface extends Window { libloki: any; displayNameRegex: any; - Signal: any; + Signal: SignalInterface; Whisper: any; ConversationController: any;