diff --git a/background.html b/background.html
index 2fc413092..e8e85a4b4 100644
--- a/background.html
+++ b/background.html
@@ -16,7 +16,7 @@
img-src 'self' blob: data:;
media-src 'self' blob:;
object-src 'none';
- script-src 'self';
+ script-src 'self' 'unsafe-eval';
style-src 'self' 'unsafe-inline';"
>
Session
diff --git a/ts/receiver/mediumGroups.ts b/ts/receiver/mediumGroups.ts
index 509d154de..cfec33351 100644
--- a/ts/receiver/mediumGroups.ts
+++ b/ts/receiver/mediumGroups.ts
@@ -8,19 +8,13 @@ import _ from 'lodash';
import * as SenderKeyAPI from '../session/medium_group';
import { getChainKey } from '../session/medium_group/ratchet';
-import { StringUtils } from '../session/utils';
-import { BufferType } from '../session/utils/String';
-import { ConversationModel } from '../../js/models/conversations';
+import { fromHex, toHex } from '../session/utils/String';
import { UserUtil } from '../util';
import {
createSenderKeyForGroup,
- RatchetState,
shareSenderKeys,
} from '../session/medium_group/senderKeys';
-const toHex = (d: BufferType) => StringUtils.decode(d, 'hex');
-const fromHex = (d: string) => StringUtils.encode(d, 'hex');
-
async function handleSenderKeyRequest(
envelope: EnvelopePlus,
groupUpdate: SignalService.MediumGroupUpdate
diff --git a/ts/session/crypto/MessageEncrypter.ts b/ts/session/crypto/MessageEncrypter.ts
index 7e4edaa90..066ed5c0b 100644
--- a/ts/session/crypto/MessageEncrypter.ts
+++ b/ts/session/crypto/MessageEncrypter.ts
@@ -6,6 +6,8 @@ import { encryptWithSenderKey } from '../../session/medium_group/ratchet';
import { PubKey } from '../types';
import { StringUtils } from '../utils';
+import * as libsodiumwrappers from 'libsodium-wrappers';
+
/**
* Add padding to a message buffer
* @param messageBuffer The buffer to add padding to.
@@ -69,6 +71,76 @@ export async function encrypt(
return encryptUsingSealedSender(device, innerCipherText);
}
+async function getSodium(): Promise {
+ await libsodiumwrappers.ready;
+ return libsodiumwrappers;
+}
+
+const concatUInt8Array = (...args: Array): Uint8Array => {
+ const totalLength = args.reduce((acc, current) => acc + current.length, 0);
+
+ const concatted = new Uint8Array(totalLength);
+ let currentIndex = 0;
+ args.forEach(arr => {
+ concatted.set(arr, currentIndex);
+ currentIndex += arr.length;
+ });
+
+ return concatted;
+};
+
+export async function encryptUsingSessionProtocol(
+ recipientHexEncodedX25519PublicKey: PubKey,
+ plaintext: Uint8Array
+): Promise {
+ const userED25519KeyPairHex = await UserUtil.getUserED25519KeyPair();
+ if (!userED25519KeyPairHex) {
+ throw new Error("Couldn't find user ED25519 key pair.");
+ }
+ const sodium = await getSodium();
+
+ const recipientX25519PublicKeyWithoutPrefix = PubKey.remove05PrefixIfNeeded(
+ recipientHexEncodedX25519PublicKey.key
+ );
+
+ const recipientX25519PublicKey = new Uint8Array(
+ StringUtils.fromHex(recipientX25519PublicKeyWithoutPrefix)
+ );
+ const userED25519PubKeyBytes = new Uint8Array(
+ StringUtils.fromHex(userED25519KeyPairHex.pubKey)
+ );
+ const userED25519SecretKeyBytes = new Uint8Array(
+ StringUtils.fromHex(userED25519KeyPairHex.privKey)
+ );
+
+ // merge all arrays into one
+ const data = concatUInt8Array(
+ plaintext,
+ userED25519PubKeyBytes,
+ recipientX25519PublicKey
+ );
+
+ const signature = sodium.crypto_sign(data, userED25519SecretKeyBytes);
+ if (!signature) {
+ throw new Error("Couldn't sign message");
+ }
+
+ const dataForBoxSeal = concatUInt8Array(
+ plaintext,
+ userED25519PubKeyBytes,
+ signature
+ );
+
+ const ciphertext = sodium.crypto_box_seal(
+ dataForBoxSeal,
+ recipientX25519PublicKey
+ );
+ if (!ciphertext) {
+ throw new Error("Couldn't encrypt message.");
+ }
+ return ciphertext;
+}
+
export async function encryptForMediumGroup(
device: PubKey,
plainTextBuffer: Uint8Array
diff --git a/ts/session/types/PubKey.ts b/ts/session/types/PubKey.ts
index 7e63b32e9..304c7bb42 100644
--- a/ts/session/types/PubKey.ts
+++ b/ts/session/types/PubKey.ts
@@ -68,6 +68,13 @@ export class PubKey {
return this.regex.test(pubkeyString);
}
+ public static remove05PrefixIfNeeded(recipient: string): string {
+ if (recipient.length === 66 && recipient.startsWith('05')) {
+ return recipient.substr(2);
+ }
+ return recipient;
+ }
+
public isEqual(comparator: PubKey | string) {
return comparator instanceof PubKey
? this.key === comparator.key
diff --git a/ts/session/utils/String.ts b/ts/session/utils/String.ts
index 120d4e91a..8a4ea7f69 100644
--- a/ts/session/utils/String.ts
+++ b/ts/session/utils/String.ts
@@ -29,3 +29,6 @@ export function decode(buffer: BufferType, stringEncoding: Encoding): string {
export function nonNullish(v: V): v is NonNullable {
return v !== undefined && v !== null;
}
+
+export const toHex = (d: BufferType) => decode(d, 'hex');
+export const fromHex = (d: string) => encode(d, 'hex');
diff --git a/ts/util/user.ts b/ts/util/user.ts
index 0d074af61..ec107ab3c 100644
--- a/ts/util/user.ts
+++ b/ts/util/user.ts
@@ -2,6 +2,13 @@ import { getItemById } from '../../js/modules/data';
import { KeyPair } from '../../libtextsecure/libsignal-protocol';
import { PrimaryPubKey } from '../session/types';
import { MultiDeviceProtocol } from '../session/protocols';
+import { StringUtils } from '../session/utils';
+import _ from 'lodash';
+
+export type HexKeyPair = {
+ pubKey: string;
+ privKey: string;
+};
export async function getCurrentDevicePubKey(): Promise {
const item = await getItemById('number_id');
@@ -17,8 +24,27 @@ export async function getPrimary(): Promise {
return MultiDeviceProtocol.getPrimaryDevice(ourNumber);
}
+/**
+ * This return the stored x25519 identity keypair for that user
+ */
export async function getIdentityKeyPair(): Promise {
const item = await getItemById('identityKey');
return item?.value;
}
+
+export async function getUserED25519KeyPair(): Promise {
+ // 'identityKey' keeps the ed25519KeyPair under a ed25519KeyPair field.
+ // it is only set if the user migrated to the ed25519 way of generating a key
+ const item = await getItemById('identityKey');
+ const ed25519KeyPair = item?.value?.ed25519KeyPair;
+ if (ed25519KeyPair?.publicKey && ed25519KeyPair?.privateKey) {
+ const pubKeyAsArray = _.map(ed25519KeyPair.publicKey, a => a);
+ const privKeyAsArray = _.map(ed25519KeyPair.privateKey, a => a);
+ return {
+ pubKey: StringUtils.toHex(new Uint8Array(pubKeyAsArray)),
+ privKey: StringUtils.toHex(new Uint8Array(privKeyAsArray)),
+ };
+ }
+ return undefined;
+}