add the encryptWithSessionProtocol method

This commit is contained in:
Audric Ackermann 2020-12-15 17:15:00 +11:00
parent cf25e89a14
commit cd3ec67099
6 changed files with 110 additions and 8 deletions

View File

@ -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';"
>
<title>Session</title>

View File

@ -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

View File

@ -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<typeof libsodiumwrappers> {
await libsodiumwrappers.ready;
return libsodiumwrappers;
}
const concatUInt8Array = (...args: Array<Uint8Array>): 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<Uint8Array> {
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

View File

@ -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

View File

@ -29,3 +29,6 @@ export function decode(buffer: BufferType, stringEncoding: Encoding): string {
export function nonNullish<V>(v: V): v is NonNullable<V> {
return v !== undefined && v !== null;
}
export const toHex = (d: BufferType) => decode(d, 'hex');
export const fromHex = (d: string) => encode(d, 'hex');

View File

@ -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<string | undefined> {
const item = await getItemById('number_id');
@ -17,8 +24,27 @@ export async function getPrimary(): Promise<PrimaryPubKey> {
return MultiDeviceProtocol.getPrimaryDevice(ourNumber);
}
/**
* This return the stored x25519 identity keypair for that user
*/
export async function getIdentityKeyPair(): Promise<KeyPair | undefined> {
const item = await getItemById('identityKey');
return item?.value;
}
export async function getUserED25519KeyPair(): Promise<HexKeyPair | undefined> {
// '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;
}