Merge pull request #1176 from bemusementpark/sync-everything

Synchronize usage of Cipher
This commit is contained in:
Morgan Pretty
2023-05-30 12:22:28 +10:00
committed by GitHub
12 changed files with 137 additions and 152 deletions

View File

@@ -14,6 +14,7 @@ import org.session.libsession.avatars.AvatarHelper
import org.session.libsession.messaging.sending_receiving.attachments.AttachmentId
import org.session.libsession.utilities.Conversions
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.utilities.ByteUtil
import org.session.libsignal.utilities.Log
@@ -278,7 +279,7 @@ object FullBackupExporter {
return false
}
private class BackupFrameOutputStream : Closeable, Flushable {
private class BackupFrameOutputStream(outputStream: OutputStream, passphrase: String) : Closeable, Flushable {
private val outputStream: OutputStream
private var cipher: Cipher
@@ -289,7 +290,7 @@ object FullBackupExporter {
private var counter: Int = 0
constructor(outputStream: OutputStream, passphrase: String) : super() {
init {
try {
val salt = Util.getSecretBytes(32)
val key = BackupUtil.computeBackupKey(passphrase, salt)
@@ -381,18 +382,24 @@ object FullBackupExporter {
private fun writeStream(inputStream: InputStream) {
try {
Conversions.intToByteArray(iv, 0, counter++)
cipher.init(Cipher.ENCRYPT_MODE, SecretKeySpec(cipherKey, "AES"), IvParameterSpec(iv))
mac.update(iv)
val buffer = ByteArray(8192)
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)
val remainder = synchronized(CIPHER_LOCK) {
cipher.init(
Cipher.ENCRYPT_MODE,
SecretKeySpec(cipherKey, "AES"),
IvParameterSpec(iv)
)
mac.update(iv)
val buffer = ByteArray(8192)
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)
mac.update(remainder)
val attachmentDigest = mac.doFinal()
@@ -414,8 +421,10 @@ object FullBackupExporter {
private fun write(out: OutputStream, frame: BackupFrame) {
try {
Conversions.intToByteArray(iv, 0, counter++)
cipher.init(Cipher.ENCRYPT_MODE, SecretKeySpec(cipherKey, "AES"), IvParameterSpec(iv))
val frameCiphertext = cipher.doFinal(frame.toByteArray())
val frameCiphertext = synchronized(CIPHER_LOCK) {
cipher.init(Cipher.ENCRYPT_MODE, SecretKeySpec(cipherKey, "AES"), IvParameterSpec(iv))
cipher.doFinal(frame.toByteArray())
}
val frameMac = mac.doFinal(frameCiphertext)
val length = Conversions.intToByteArray(frameCiphertext.size + 10)
out.write(length)

View File

@@ -12,6 +12,7 @@ import org.session.libsession.messaging.sending_receiving.attachments.Attachment
import org.session.libsession.utilities.Address
import org.session.libsession.utilities.Conversions
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.utilities.ByteUtil
import org.session.libsignal.utilities.Log
@@ -243,7 +244,7 @@ object FullBackupImporter {
val split = ByteUtil.split(derived, 32, 32)
cipherKey = split[0]
macKey = split[1]
cipher = Cipher.getInstance("AES/CTR/NoPadding")
cipher = synchronized(CIPHER_LOCK) { Cipher.getInstance("AES/CTR/NoPadding") }
mac = Mac.getInstance("HmacSHA256")
mac.init(SecretKeySpec(macKey, "HmacSHA256"))
counter = Conversions.byteArrayToInt(iv)
@@ -269,20 +270,26 @@ object FullBackupImporter {
var length = length
try {
Conversions.intToByteArray(iv, 0, counter++)
cipher.init(Cipher.DECRYPT_MODE, SecretKeySpec(cipherKey, "AES"), IvParameterSpec(iv))
mac.update(iv)
val buffer = ByteArray(8192)
while (length > 0) {
val read = inputStream.read(buffer, 0, Math.min(buffer.size, length))
if (read == -1) throw IOException("File ended early!")
mac.update(buffer, 0, read)
val plaintext = cipher.update(buffer, 0, read)
if (plaintext != null) {
out.write(plaintext, 0, plaintext.size)
val plaintext = synchronized(CIPHER_LOCK) {
cipher.init(
Cipher.DECRYPT_MODE,
SecretKeySpec(cipherKey, "AES"),
IvParameterSpec(iv)
)
mac.update(iv)
val buffer = ByteArray(8192)
while (length > 0) {
val read = inputStream.read(buffer, 0, Math.min(buffer.size, length))
if (read == -1) throw IOException("File ended early!")
mac.update(buffer, 0, read)
val plaintext = cipher.update(buffer, 0, read)
if (plaintext != null) {
out.write(plaintext, 0, plaintext.size)
}
length -= read
}
length -= read
cipher.doFinal()
}
val plaintext = cipher.doFinal()
if (plaintext != null) {
out.write(plaintext, 0, plaintext.size)
}
@@ -325,8 +332,10 @@ object FullBackupImporter {
throw IOException("Bad MAC")
}
Conversions.intToByteArray(iv, 0, counter++)
cipher.init(Cipher.DECRYPT_MODE, SecretKeySpec(cipherKey, "AES"), IvParameterSpec(iv))
val plaintext = cipher.doFinal(frame, 0, frame.size - 10)
val plaintext = synchronized(CIPHER_LOCK) {
cipher.init(Cipher.DECRYPT_MODE, SecretKeySpec(cipherKey, "AES"), IvParameterSpec(iv))
cipher.doFinal(frame, 0, frame.size - 10)
}
BackupFrame.parseFrom(plaintext)
} catch (e: Exception) {
when (e) {

View File

@@ -1,13 +1,13 @@
package org.thoughtcrime.securesms.crypto;
import android.os.Build;
import static org.session.libsignal.crypto.CipherUtil.CIPHER_LOCK;
import android.security.keystore.KeyGenParameterSpec;
import android.security.keystore.KeyProperties;
import android.util.Base64;
import androidx.annotation.NonNull;
import androidx.annotation.RequiresApi;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.core.JsonGenerator;
@@ -45,8 +45,6 @@ public final class KeyStoreHelper {
private static final String ANDROID_KEY_STORE = "AndroidKeyStore";
private static final String KEY_ALIAS = "SignalSecret";
private static final Object lock = new Object();
public static SealedData seal(@NonNull byte[] input) {
SecretKey secretKey = getOrCreateKeyStoreEntry();
@@ -54,7 +52,7 @@ public final class KeyStoreHelper {
// Cipher operations are not thread-safe so we synchronize over them through doFinal to
// prevent crashes with quickly repeated encrypt/decrypt operations
// https://github.com/mozilla-mobile/android-components/issues/5342
synchronized (lock) {
synchronized (CIPHER_LOCK) {
Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");
cipher.init(Cipher.ENCRYPT_MODE, secretKey);
@@ -75,7 +73,7 @@ public final class KeyStoreHelper {
// Cipher operations are not thread-safe so we synchronize over them through doFinal to
// prevent crashes with quickly repeated encrypt/decrypt operations
// https://github.com/mozilla-mobile/android-components/issues/5342
synchronized (lock) {
synchronized (CIPHER_LOCK) {
Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");
cipher.init(Cipher.DECRYPT_MODE, secretKey, new GCMParameterSpec(128, sealedData.iv));
@@ -208,7 +206,5 @@ public final class KeyStoreHelper {
return Base64.decode(p.getValueAsString(), Base64.NO_WRAP | Base64.NO_PADDING);
}
}
}
}

View File

@@ -1,5 +1,7 @@
package org.thoughtcrime.securesms.logging;
import static org.session.libsignal.crypto.CipherUtil.CIPHER_LOCK;
import androidx.annotation.NonNull;
import org.session.libsession.utilities.Conversions;
@@ -66,15 +68,17 @@ class LogFile {
byte[] plaintext = entry.getBytes();
try {
cipher.init(Cipher.ENCRYPT_MODE, new SecretKeySpec(secret, "AES"), new IvParameterSpec(ivBuffer));
synchronized (CIPHER_LOCK) {
cipher.init(Cipher.ENCRYPT_MODE, new SecretKeySpec(secret, "AES"), new IvParameterSpec(ivBuffer));
int cipherLength = cipher.getOutputSize(plaintext.length);
byte[] ciphertext = ciphertextBuffer.get(cipherLength);
cipherLength = cipher.doFinal(plaintext, 0, plaintext.length, ciphertext);
int cipherLength = cipher.getOutputSize(plaintext.length);
byte[] ciphertext = ciphertextBuffer.get(cipherLength);
cipherLength = cipher.doFinal(plaintext, 0, plaintext.length, ciphertext);
outputStream.write(ivBuffer);
outputStream.write(Conversions.intToByteArray(cipherLength));
outputStream.write(ciphertext, 0, cipherLength);
outputStream.write(ivBuffer);
outputStream.write(Conversions.intToByteArray(cipherLength));
outputStream.write(ciphertext, 0, cipherLength);
}
outputStream.flush();
} catch (ShortBufferException | InvalidAlgorithmParameterException | InvalidKeyException | BadPaddingException | IllegalBlockSizeException e) {
@@ -134,10 +138,11 @@ class LogFile {
Util.readFully(inputStream, ciphertext, length);
try {
cipher.init(Cipher.DECRYPT_MODE, new SecretKeySpec(secret, "AES"), new IvParameterSpec(ivBuffer));
byte[] plaintext = cipher.doFinal(ciphertext, 0, length);
return new String(plaintext);
synchronized (CIPHER_LOCK) {
cipher.init(Cipher.DECRYPT_MODE, new SecretKeySpec(secret, "AES"), new IvParameterSpec(ivBuffer));
byte[] plaintext = cipher.doFinal(ciphertext, 0, length);
return new String(plaintext);
}
} catch (InvalidKeyException | InvalidAlgorithmParameterException | IllegalBlockSizeException | BadPaddingException e) {
throw new AssertionError(e);
}