mirror of
https://github.com/oxen-io/session-android.git
synced 2024-11-23 18:15:22 +00:00
Synchronize all Cipher#doFinal
This commit is contained in:
parent
99cb10f5be
commit
d3ce899a80
@ -14,6 +14,7 @@ import org.session.libsession.avatars.AvatarHelper
|
|||||||
import org.session.libsession.messaging.sending_receiving.attachments.AttachmentId
|
import org.session.libsession.messaging.sending_receiving.attachments.AttachmentId
|
||||||
import org.session.libsession.utilities.Conversions
|
import org.session.libsession.utilities.Conversions
|
||||||
import org.session.libsession.utilities.Util
|
import org.session.libsession.utilities.Util
|
||||||
|
import org.session.libsignal.crypto.CipherUtil.CIPHER_LOCK
|
||||||
import org.session.libsignal.crypto.kdf.HKDFv3
|
import org.session.libsignal.crypto.kdf.HKDFv3
|
||||||
import org.session.libsignal.utilities.ByteUtil
|
import org.session.libsignal.utilities.ByteUtil
|
||||||
import org.session.libsignal.utilities.Log
|
import org.session.libsignal.utilities.Log
|
||||||
@ -289,7 +290,7 @@ object FullBackupExporter {
|
|||||||
|
|
||||||
private var counter: Int = 0
|
private var counter: Int = 0
|
||||||
|
|
||||||
constructor(outputStream: OutputStream, passphrase: String) : super() {
|
private constructor(outputStream: OutputStream, passphrase: String) : super() {
|
||||||
try {
|
try {
|
||||||
val salt = Util.getSecretBytes(32)
|
val salt = Util.getSecretBytes(32)
|
||||||
val key = BackupUtil.computeBackupKey(passphrase, salt)
|
val key = BackupUtil.computeBackupKey(passphrase, salt)
|
||||||
@ -381,18 +382,24 @@ object FullBackupExporter {
|
|||||||
private fun writeStream(inputStream: InputStream) {
|
private fun writeStream(inputStream: InputStream) {
|
||||||
try {
|
try {
|
||||||
Conversions.intToByteArray(iv, 0, counter++)
|
Conversions.intToByteArray(iv, 0, counter++)
|
||||||
cipher.init(Cipher.ENCRYPT_MODE, SecretKeySpec(cipherKey, "AES"), IvParameterSpec(iv))
|
val remainder = synchronized(CIPHER_LOCK) {
|
||||||
mac.update(iv)
|
cipher.init(
|
||||||
val buffer = ByteArray(8192)
|
Cipher.ENCRYPT_MODE,
|
||||||
var read: Int
|
SecretKeySpec(cipherKey, "AES"),
|
||||||
while (inputStream.read(buffer).also { read = it } != -1) {
|
IvParameterSpec(iv)
|
||||||
val ciphertext = cipher.update(buffer, 0, read)
|
)
|
||||||
if (ciphertext != null) {
|
mac.update(iv)
|
||||||
outputStream.write(ciphertext)
|
val buffer = ByteArray(8192)
|
||||||
mac.update(ciphertext)
|
var read: Int
|
||||||
|
while (inputStream.read(buffer).also { read = it } != -1) {
|
||||||
|
val ciphertext = cipher.update(buffer, 0, read)
|
||||||
|
if (ciphertext != null) {
|
||||||
|
outputStream.write(ciphertext)
|
||||||
|
mac.update(ciphertext)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
cipher.doFinal()
|
||||||
}
|
}
|
||||||
val remainder = cipher.doFinal()
|
|
||||||
outputStream.write(remainder)
|
outputStream.write(remainder)
|
||||||
mac.update(remainder)
|
mac.update(remainder)
|
||||||
val attachmentDigest = mac.doFinal()
|
val attachmentDigest = mac.doFinal()
|
||||||
@ -414,8 +421,10 @@ object FullBackupExporter {
|
|||||||
private fun write(out: OutputStream, frame: BackupFrame) {
|
private fun write(out: OutputStream, frame: BackupFrame) {
|
||||||
try {
|
try {
|
||||||
Conversions.intToByteArray(iv, 0, counter++)
|
Conversions.intToByteArray(iv, 0, counter++)
|
||||||
cipher.init(Cipher.ENCRYPT_MODE, SecretKeySpec(cipherKey, "AES"), IvParameterSpec(iv))
|
val frameCiphertext = synchronized(CIPHER_LOCK) {
|
||||||
val frameCiphertext = cipher.doFinal(frame.toByteArray())
|
cipher.init(Cipher.ENCRYPT_MODE, SecretKeySpec(cipherKey, "AES"), IvParameterSpec(iv))
|
||||||
|
cipher.doFinal(frame.toByteArray())
|
||||||
|
}
|
||||||
val frameMac = mac.doFinal(frameCiphertext)
|
val frameMac = mac.doFinal(frameCiphertext)
|
||||||
val length = Conversions.intToByteArray(frameCiphertext.size + 10)
|
val length = Conversions.intToByteArray(frameCiphertext.size + 10)
|
||||||
out.write(length)
|
out.write(length)
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
package org.session.libsession.utilities
|
package org.session.libsession.utilities
|
||||||
|
|
||||||
import androidx.annotation.WorkerThread
|
import androidx.annotation.WorkerThread
|
||||||
|
import org.session.libsignal.crypto.CipherUtil.CIPHER_LOCK
|
||||||
import org.session.libsignal.utilities.ByteUtil
|
import org.session.libsignal.utilities.ByteUtil
|
||||||
import org.session.libsignal.utilities.Util
|
import org.session.libsignal.utilities.Util
|
||||||
import org.session.libsignal.utilities.Hex
|
import org.session.libsignal.utilities.Hex
|
||||||
@ -27,9 +28,11 @@ internal object AESGCM {
|
|||||||
internal fun decrypt(ivAndCiphertext: ByteArray, symmetricKey: ByteArray): ByteArray {
|
internal fun decrypt(ivAndCiphertext: ByteArray, symmetricKey: ByteArray): ByteArray {
|
||||||
val iv = ivAndCiphertext.sliceArray(0 until ivSize)
|
val iv = ivAndCiphertext.sliceArray(0 until ivSize)
|
||||||
val ciphertext = ivAndCiphertext.sliceArray(ivSize until ivAndCiphertext.count())
|
val ciphertext = ivAndCiphertext.sliceArray(ivSize until ivAndCiphertext.count())
|
||||||
val cipher = Cipher.getInstance("AES/GCM/NoPadding")
|
synchronized(CIPHER_LOCK) {
|
||||||
cipher.init(Cipher.DECRYPT_MODE, SecretKeySpec(symmetricKey, "AES"), GCMParameterSpec(gcmTagSize, iv))
|
val cipher = Cipher.getInstance("AES/GCM/NoPadding")
|
||||||
return cipher.doFinal(ciphertext)
|
cipher.init(Cipher.DECRYPT_MODE, SecretKeySpec(symmetricKey, "AES"), GCMParameterSpec(gcmTagSize, iv))
|
||||||
|
return cipher.doFinal(ciphertext)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -47,9 +50,11 @@ internal object AESGCM {
|
|||||||
*/
|
*/
|
||||||
internal fun encrypt(plaintext: ByteArray, symmetricKey: ByteArray): ByteArray {
|
internal fun encrypt(plaintext: ByteArray, symmetricKey: ByteArray): ByteArray {
|
||||||
val iv = Util.getSecretBytes(ivSize)
|
val iv = Util.getSecretBytes(ivSize)
|
||||||
val cipher = Cipher.getInstance("AES/GCM/NoPadding")
|
synchronized(CIPHER_LOCK) {
|
||||||
cipher.init(Cipher.ENCRYPT_MODE, SecretKeySpec(symmetricKey, "AES"), GCMParameterSpec(gcmTagSize, iv))
|
val cipher = Cipher.getInstance("AES/GCM/NoPadding")
|
||||||
return ByteUtil.combine(iv, cipher.doFinal(plaintext))
|
cipher.init(Cipher.ENCRYPT_MODE, SecretKeySpec(symmetricKey, "AES"), GCMParameterSpec(gcmTagSize, iv))
|
||||||
|
return ByteUtil.combine(iv, cipher.doFinal(plaintext))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -0,0 +1,5 @@
|
|||||||
|
package org.session.libsignal.crypto;
|
||||||
|
|
||||||
|
public class CipherUtil {
|
||||||
|
public static final Object CIPHER_LOCK = new Object();
|
||||||
|
}
|
@ -1,45 +0,0 @@
|
|||||||
package org.session.libsignal.crypto
|
|
||||||
|
|
||||||
import org.whispersystems.curve25519.Curve25519
|
|
||||||
import org.session.libsignal.utilities.Util
|
|
||||||
import javax.crypto.Cipher
|
|
||||||
import javax.crypto.spec.IvParameterSpec
|
|
||||||
import javax.crypto.spec.SecretKeySpec
|
|
||||||
|
|
||||||
object DiffieHellman {
|
|
||||||
private val cipher = Cipher.getInstance("AES/CBC/PKCS5Padding")
|
|
||||||
private val curve = Curve25519.getInstance(Curve25519.BEST)
|
|
||||||
private val ivSize = 16
|
|
||||||
|
|
||||||
@JvmStatic @Throws
|
|
||||||
fun encrypt(plaintext: ByteArray, symmetricKey: ByteArray): ByteArray {
|
|
||||||
val iv = Util.getSecretBytes(ivSize)
|
|
||||||
val ivSpec = IvParameterSpec(iv)
|
|
||||||
val secretKeySpec = SecretKeySpec(symmetricKey, "AES")
|
|
||||||
cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec, ivSpec)
|
|
||||||
val ciphertext = cipher.doFinal(plaintext)
|
|
||||||
return iv + ciphertext
|
|
||||||
}
|
|
||||||
|
|
||||||
@JvmStatic @Throws
|
|
||||||
fun encrypt(plaintext: ByteArray, publicKey: ByteArray, privateKey: ByteArray): ByteArray {
|
|
||||||
val symmetricKey = curve.calculateAgreement(publicKey, privateKey)
|
|
||||||
return encrypt(plaintext, symmetricKey)
|
|
||||||
}
|
|
||||||
|
|
||||||
@JvmStatic @Throws
|
|
||||||
fun decrypt(ivAndCiphertext: ByteArray, symmetricKey: ByteArray): ByteArray {
|
|
||||||
val iv = ivAndCiphertext.sliceArray(0 until ivSize)
|
|
||||||
val ciphertext = ivAndCiphertext.sliceArray(ivSize until ivAndCiphertext.size)
|
|
||||||
val ivSpec = IvParameterSpec(iv)
|
|
||||||
val secretKeySpec = SecretKeySpec(symmetricKey, "AES")
|
|
||||||
cipher.init(Cipher.DECRYPT_MODE, secretKeySpec, ivSpec)
|
|
||||||
return cipher.doFinal(ciphertext)
|
|
||||||
}
|
|
||||||
|
|
||||||
@JvmStatic @Throws
|
|
||||||
fun decrypt(ivAndCiphertext: ByteArray, publicKey: ByteArray, privateKey: ByteArray): ByteArray {
|
|
||||||
val symmetricKey = curve.calculateAgreement(publicKey, privateKey)
|
|
||||||
return decrypt(ivAndCiphertext, symmetricKey)
|
|
||||||
}
|
|
||||||
}
|
|
@ -39,9 +39,7 @@ public abstract class HKDF {
|
|||||||
Mac mac = Mac.getInstance("HmacSHA256");
|
Mac mac = Mac.getInstance("HmacSHA256");
|
||||||
mac.init(new SecretKeySpec(salt, "HmacSHA256"));
|
mac.init(new SecretKeySpec(salt, "HmacSHA256"));
|
||||||
return mac.doFinal(inputKeyMaterial);
|
return mac.doFinal(inputKeyMaterial);
|
||||||
} catch (NoSuchAlgorithmException e) {
|
} catch (NoSuchAlgorithmException | InvalidKeyException e) {
|
||||||
throw new AssertionError(e);
|
|
||||||
} catch (InvalidKeyException e) {
|
|
||||||
throw new AssertionError(e);
|
throw new AssertionError(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -73,9 +71,7 @@ public abstract class HKDF {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return results.toByteArray();
|
return results.toByteArray();
|
||||||
} catch (NoSuchAlgorithmException e) {
|
} catch (NoSuchAlgorithmException | InvalidKeyException e) {
|
||||||
throw new AssertionError(e);
|
|
||||||
} catch (InvalidKeyException e) {
|
|
||||||
throw new AssertionError(e);
|
throw new AssertionError(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -6,6 +6,8 @@
|
|||||||
|
|
||||||
package org.session.libsignal.streams;
|
package org.session.libsignal.streams;
|
||||||
|
|
||||||
|
import static org.session.libsignal.crypto.CipherUtil.CIPHER_LOCK;
|
||||||
|
|
||||||
import org.session.libsignal.utilities.Util;
|
import org.session.libsignal.utilities.Util;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
@ -68,16 +70,17 @@ public class AttachmentCipherOutputStream extends DigestingOutputStream {
|
|||||||
@Override
|
@Override
|
||||||
public void flush() throws IOException {
|
public void flush() throws IOException {
|
||||||
try {
|
try {
|
||||||
byte[] ciphertext = cipher.doFinal();
|
byte[] ciphertext;
|
||||||
|
synchronized (CIPHER_LOCK) {
|
||||||
|
ciphertext = cipher.doFinal();
|
||||||
|
}
|
||||||
byte[] auth = mac.doFinal(ciphertext);
|
byte[] auth = mac.doFinal(ciphertext);
|
||||||
|
|
||||||
super.write(ciphertext);
|
super.write(ciphertext);
|
||||||
super.write(auth);
|
super.write(auth);
|
||||||
|
|
||||||
super.flush();
|
super.flush();
|
||||||
} catch (IllegalBlockSizeException e) {
|
} catch (IllegalBlockSizeException | BadPaddingException e) {
|
||||||
throw new AssertionError(e);
|
|
||||||
} catch (BadPaddingException e) {
|
|
||||||
throw new AssertionError(e);
|
throw new AssertionError(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -97,9 +100,7 @@ public class AttachmentCipherOutputStream extends DigestingOutputStream {
|
|||||||
private Cipher initializeCipher() {
|
private Cipher initializeCipher() {
|
||||||
try {
|
try {
|
||||||
return Cipher.getInstance("AES/CBC/PKCS5Padding");
|
return Cipher.getInstance("AES/CBC/PKCS5Padding");
|
||||||
} catch (NoSuchAlgorithmException e) {
|
} catch (NoSuchAlgorithmException | NoSuchPaddingException e) {
|
||||||
throw new AssertionError(e);
|
|
||||||
} catch (NoSuchPaddingException e) {
|
|
||||||
throw new AssertionError(e);
|
throw new AssertionError(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,7 @@
|
|||||||
package org.session.libsignal.streams;
|
package org.session.libsignal.streams;
|
||||||
|
|
||||||
|
import static org.session.libsignal.crypto.CipherUtil.CIPHER_LOCK;
|
||||||
|
|
||||||
import org.session.libsignal.utilities.Util;
|
import org.session.libsignal.utilities.Util;
|
||||||
|
|
||||||
import java.io.FilterInputStream;
|
import java.io.FilterInputStream;
|
||||||
@ -62,23 +64,23 @@ public class ProfileCipherInputStream extends FilterInputStream {
|
|||||||
byte[] ciphertext = new byte[outputLength / 2];
|
byte[] ciphertext = new byte[outputLength / 2];
|
||||||
int read = in.read(ciphertext, 0, ciphertext.length);
|
int read = in.read(ciphertext, 0, ciphertext.length);
|
||||||
|
|
||||||
if (read == -1) {
|
synchronized (CIPHER_LOCK) {
|
||||||
if (cipher.getOutputSize(0) > outputLength) {
|
if (read == -1) {
|
||||||
throw new AssertionError("Need: " + cipher.getOutputSize(0) + " but only have: " + outputLength);
|
if (cipher.getOutputSize(0) > outputLength) {
|
||||||
}
|
throw new AssertionError("Need: " + cipher.getOutputSize(0) + " but only have: " + outputLength);
|
||||||
|
}
|
||||||
|
|
||||||
finished = true;
|
finished = true;
|
||||||
return cipher.doFinal(output, outputOffset);
|
return cipher.doFinal(output, outputOffset);
|
||||||
} else {
|
} else {
|
||||||
if (cipher.getOutputSize(read) > outputLength) {
|
if (cipher.getOutputSize(read) > outputLength) {
|
||||||
throw new AssertionError("Need: " + cipher.getOutputSize(read) + " but only have: " + outputLength);
|
throw new AssertionError("Need: " + cipher.getOutputSize(read) + " but only have: " + outputLength);
|
||||||
}
|
}
|
||||||
|
|
||||||
return cipher.update(ciphertext, 0, read, output, outputOffset);
|
return cipher.update(ciphertext, 0, read, output, outputOffset);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} catch (IllegalBlockSizeException e) {
|
} catch (IllegalBlockSizeException | ShortBufferException e) {
|
||||||
throw new AssertionError(e);
|
|
||||||
} catch(ShortBufferException e) {
|
|
||||||
throw new AssertionError(e);
|
throw new AssertionError(e);
|
||||||
} catch (BadPaddingException e) {
|
} catch (BadPaddingException e) {
|
||||||
throw new IOException(e);
|
throw new IOException(e);
|
||||||
|
@ -1,5 +1,7 @@
|
|||||||
package org.session.libsignal.streams;
|
package org.session.libsignal.streams;
|
||||||
|
|
||||||
|
import static org.session.libsignal.crypto.CipherUtil.CIPHER_LOCK;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.OutputStream;
|
import java.io.OutputStream;
|
||||||
import java.security.InvalidAlgorithmParameterException;
|
import java.security.InvalidAlgorithmParameterException;
|
||||||
@ -54,20 +56,24 @@ public class ProfileCipherOutputStream extends DigestingOutputStream {
|
|||||||
byte[] input = new byte[1];
|
byte[] input = new byte[1];
|
||||||
input[0] = (byte)b;
|
input[0] = (byte)b;
|
||||||
|
|
||||||
byte[] output = cipher.update(input);
|
byte[] output;
|
||||||
|
synchronized (CIPHER_LOCK) {
|
||||||
|
output = cipher.update(input);
|
||||||
|
}
|
||||||
super.write(output);
|
super.write(output);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void flush() throws IOException {
|
public void flush() throws IOException {
|
||||||
try {
|
try {
|
||||||
byte[] output = cipher.doFinal();
|
byte[] output;
|
||||||
|
synchronized (CIPHER_LOCK) {
|
||||||
|
output = cipher.doFinal();
|
||||||
|
}
|
||||||
|
|
||||||
super.write(output);
|
super.write(output);
|
||||||
super.flush();
|
super.flush();
|
||||||
} catch (BadPaddingException e) {
|
} catch (BadPaddingException | IllegalBlockSizeException e) {
|
||||||
throw new AssertionError(e);
|
|
||||||
} catch (IllegalBlockSizeException e) {
|
|
||||||
throw new AssertionError(e);
|
throw new AssertionError(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user