You cannot select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
		
			
		
			
				
	
	
		
			105 lines
		
	
	
		
			3.6 KiB
		
	
	
	
		
			TypeScript
		
	
			
		
		
	
	
			105 lines
		
	
	
		
			3.6 KiB
		
	
	
	
		
			TypeScript
		
	
| import { SignalService } from '../../protobuf';
 | |
| import { PubKey } from '../types';
 | |
| import { concatUInt8Array, getSodiumRenderer, MessageEncrypter } from '.';
 | |
| import { fromHexToArray } from '../utils/String';
 | |
| import { Data } from '../../data/data';
 | |
| import { UserUtils } from '../utils';
 | |
| import { addMessagePadding } from './BufferPadding';
 | |
| 
 | |
| export { concatUInt8Array, getSodiumRenderer };
 | |
| 
 | |
| type EncryptResult = {
 | |
|   envelopeType: SignalService.Envelope.Type;
 | |
|   cipherText: Uint8Array;
 | |
| };
 | |
| 
 | |
| /**
 | |
|  * Encrypt `plainTextBuffer` with given `encryptionType` for `device`.
 | |
|  *
 | |
|  * @param device The device `PubKey` to encrypt for.
 | |
|  * @param plainTextBuffer The unpadded plaintext buffer. It will be padded
 | |
|  * @param encryptionType The type of encryption.
 | |
|  * @returns The envelope type and the base64 encoded cipher text
 | |
|  */
 | |
| export async function encrypt(
 | |
|   device: PubKey,
 | |
|   plainTextBuffer: Uint8Array,
 | |
|   encryptionType: SignalService.Envelope.Type
 | |
| ): Promise<EncryptResult> {
 | |
|   const { CLOSED_GROUP_MESSAGE, SESSION_MESSAGE } = SignalService.Envelope.Type;
 | |
| 
 | |
|   if (encryptionType !== CLOSED_GROUP_MESSAGE && encryptionType !== SESSION_MESSAGE) {
 | |
|     throw new Error(`Invalid encryption type:${encryptionType}`);
 | |
|   }
 | |
| 
 | |
|   const encryptForClosedGroup = encryptionType === CLOSED_GROUP_MESSAGE;
 | |
|   const plainText = addMessagePadding(plainTextBuffer);
 | |
| 
 | |
|   if (encryptForClosedGroup) {
 | |
|     // window?.log?.info(
 | |
|     //   'Encrypting message with SessionProtocol and envelope type is CLOSED_GROUP_MESSAGE'
 | |
|     // );
 | |
|     const hexEncryptionKeyPair = await Data.getLatestClosedGroupEncryptionKeyPair(device.key);
 | |
|     if (!hexEncryptionKeyPair) {
 | |
|       window?.log?.warn("Couldn't get key pair for closed group during encryption");
 | |
|       throw new Error("Couldn't get key pair for closed group");
 | |
|     }
 | |
| 
 | |
|     const hexPubFromECKeyPair = PubKey.cast(hexEncryptionKeyPair.publicHex);
 | |
| 
 | |
|     const cipherTextClosedGroup = await MessageEncrypter.encryptUsingSessionProtocol(
 | |
|       hexPubFromECKeyPair,
 | |
|       plainText
 | |
|     );
 | |
| 
 | |
|     return {
 | |
|       envelopeType: CLOSED_GROUP_MESSAGE,
 | |
|       cipherText: cipherTextClosedGroup,
 | |
|     };
 | |
|   }
 | |
|   const cipherText = await MessageEncrypter.encryptUsingSessionProtocol(device, plainText);
 | |
| 
 | |
|   return { envelopeType: SESSION_MESSAGE, cipherText };
 | |
| }
 | |
| 
 | |
| export async function encryptUsingSessionProtocol(
 | |
|   recipientHexEncodedX25519PublicKey: PubKey,
 | |
|   plaintext: Uint8Array
 | |
| ): Promise<Uint8Array> {
 | |
|   const userED25519KeyPairHex = await UserUtils.getUserED25519KeyPair();
 | |
|   if (
 | |
|     !userED25519KeyPairHex ||
 | |
|     !userED25519KeyPairHex.pubKey?.length ||
 | |
|     !userED25519KeyPairHex.privKey?.length
 | |
|   ) {
 | |
|     throw new Error("Couldn't find user ED25519 key pair.");
 | |
|   }
 | |
|   const sodium = await getSodiumRenderer();
 | |
| 
 | |
|   // window?.log?.info('encryptUsingSessionProtocol for ', recipientHexEncodedX25519PublicKey.key);
 | |
| 
 | |
|   const recipientX25519PublicKey = recipientHexEncodedX25519PublicKey.withoutPrefixToArray();
 | |
|   const userED25519PubKeyBytes = fromHexToArray(userED25519KeyPairHex.pubKey);
 | |
|   const userED25519SecretKeyBytes = fromHexToArray(userED25519KeyPairHex.privKey);
 | |
| 
 | |
|   // merge all arrays into one
 | |
|   const verificationData = concatUInt8Array(
 | |
|     plaintext,
 | |
|     userED25519PubKeyBytes,
 | |
|     recipientX25519PublicKey
 | |
|   );
 | |
| 
 | |
|   const signature = sodium.crypto_sign_detached(verificationData, userED25519SecretKeyBytes);
 | |
|   if (!signature || signature.length === 0) {
 | |
|     throw new Error("Couldn't sign message");
 | |
|   }
 | |
| 
 | |
|   const plaintextWithMetadata = concatUInt8Array(plaintext, userED25519PubKeyBytes, signature);
 | |
| 
 | |
|   const ciphertext = sodium.crypto_box_seal(plaintextWithMetadata, recipientX25519PublicKey);
 | |
|   if (!ciphertext) {
 | |
|     throw new Error("Couldn't encrypt message.");
 | |
|   }
 | |
|   return ciphertext;
 | |
| }
 |