diff --git a/libloki/crypto.js b/libloki/crypto.js index 99f2e1a4d..fc3ebd2c5 100644 --- a/libloki/crypto.js +++ b/libloki/crypto.js @@ -17,6 +17,7 @@ class FallBackDecryptionError extends Error {} const IV_LENGTH = 16; + const NONCE_LENGTH = 12; async function DHEncrypt(symmetricKey, plainText) { const iv = libsignal.crypto.getRandomBytes(IV_LENGTH); @@ -33,6 +34,35 @@ return ivAndCiphertext; } + async function EncryptGCM(symmetricKey, plaintext) { + + const nonce = crypto.getRandomValues(new Uint8Array(NONCE_LENGTH)); + + const key = await crypto.subtle.importKey('raw', symmetricKey, {name: 'AES-GCM'}, false, ['encrypt']); + + const ciphertext = await crypto.subtle.encrypt({name: 'AES-GCM', iv: nonce, tagLength: 128}, key, plaintext); + + const ivAndCiphertext = new Uint8Array( + NONCE_LENGTH + ciphertext.byteLength + ); + + ivAndCiphertext.set(nonce); + ivAndCiphertext.set(new Uint8Array(ciphertext), nonce.byteLength); + + return ivAndCiphertext; + } + + async function DecryptGCM(symmetricKey, ivAndCiphertext) { + + const nonce = ivAndCiphertext.slice(0, NONCE_LENGTH); + const ciphertext = ivAndCiphertext.slice(NONCE_LENGTH); + + const key = await crypto.subtle.importKey('raw', symmetricKey, {name: 'AES-GCM'}, false, ['decrypt']); + + return await crypto.subtle.decrypt({name: 'AES-GCM', iv: nonce}, key, ciphertext); + + } + async function DHDecrypt(symmetricKey, ivAndCiphertext) { const iv = ivAndCiphertext.slice(0, IV_LENGTH); const ciphertext = ivAndCiphertext.slice(IV_LENGTH); @@ -106,11 +136,17 @@ return Multibase.decode(`${base32zCode}${snodeAddressClean}`); } + function generateEphemeralKeyPair() { + let keys = libsignal.Curve.generateKeyPair(); + // Signal protocol prepends with "0x05" + keys.pubKey = keys.pubKey.slice(1); + return keys; + } + class LokiSnodeChannel { constructor() { - this._ephemeralKeyPair = libsignal.Curve.generateKeyPair(); - // Signal protocol prepends with "0x05" - this._ephemeralKeyPair.pubKey = this._ephemeralKeyPair.pubKey.slice(1); + + this._ephemeralKeyPair = generateEphemeralKeyPair(); this._ephemeralPubKeyHex = StringView.arrayBufferToHex( this._ephemeralKeyPair.pubKey ); @@ -474,7 +510,9 @@ window.libloki.crypto = { DHEncrypt, + EncryptGCM, // AES-GCM DHDecrypt, + DecryptGCM, // AES-GCM FallBackSessionCipher, FallBackDecryptionError, snodeCipher, @@ -485,6 +523,7 @@ validateAuthorisation, PairingType, LokiSessionCipher, + generateEphemeralKeyPair, // for testing _LokiSnodeChannel: LokiSnodeChannel, _decodeSnodeAddressToPubKey: decodeSnodeAddressToPubKey, diff --git a/libtextsecure/stringview.js b/libtextsecure/stringview.js index 75b00aefd..6be7d28b3 100644 --- a/libtextsecure/stringview.js +++ b/libtextsecure/stringview.js @@ -26,6 +26,7 @@ : 0; }, + // This is not a "standard" base64, do not use! base64ToBytes(sBase64, nBlocksSize) { const sB64Enc = sBase64.replace(/[^A-Za-z0-9+/]/g, ''); const nInLen = sB64Enc.length;