mirror of
https://github.com/oxen-io/session-android.git
synced 2024-12-24 00:37:47 +00:00
Merge remote-tracking branch 'upstream/dev' into bluetooth-manager-crash
# Conflicts: # app/src/main/java/org/thoughtcrime/securesms/backup/FullBackupExporter.kt # app/src/main/java/org/thoughtcrime/securesms/backup/FullBackupImporter.kt
This commit is contained in:
commit
c77d465438
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -1,6 +1,7 @@
|
||||
package org.session.libsession.utilities
|
||||
|
||||
import androidx.annotation.WorkerThread
|
||||
import org.session.libsignal.crypto.CipherUtil.CIPHER_LOCK
|
||||
import org.session.libsignal.utilities.ByteUtil
|
||||
import org.session.libsignal.utilities.Util
|
||||
import org.session.libsignal.utilities.Hex
|
||||
@ -27,9 +28,11 @@ internal object AESGCM {
|
||||
internal fun decrypt(ivAndCiphertext: ByteArray, symmetricKey: ByteArray): ByteArray {
|
||||
val iv = ivAndCiphertext.sliceArray(0 until ivSize)
|
||||
val ciphertext = ivAndCiphertext.sliceArray(ivSize until ivAndCiphertext.count())
|
||||
val cipher = Cipher.getInstance("AES/GCM/NoPadding")
|
||||
cipher.init(Cipher.DECRYPT_MODE, SecretKeySpec(symmetricKey, "AES"), GCMParameterSpec(gcmTagSize, iv))
|
||||
return cipher.doFinal(ciphertext)
|
||||
synchronized(CIPHER_LOCK) {
|
||||
val cipher = Cipher.getInstance("AES/GCM/NoPadding")
|
||||
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 {
|
||||
val iv = Util.getSecretBytes(ivSize)
|
||||
val cipher = Cipher.getInstance("AES/GCM/NoPadding")
|
||||
cipher.init(Cipher.ENCRYPT_MODE, SecretKeySpec(symmetricKey, "AES"), GCMParameterSpec(gcmTagSize, iv))
|
||||
return ByteUtil.combine(iv, cipher.doFinal(plaintext))
|
||||
synchronized(CIPHER_LOCK) {
|
||||
val cipher = Cipher.getInstance("AES/GCM/NoPadding")
|
||||
cipher.init(Cipher.ENCRYPT_MODE, SecretKeySpec(symmetricKey, "AES"), GCMParameterSpec(gcmTagSize, iv))
|
||||
return ByteUtil.combine(iv, cipher.doFinal(plaintext))
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -0,0 +1,8 @@
|
||||
package org.session.libsignal.crypto;
|
||||
|
||||
public class CipherUtil {
|
||||
// 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
|
||||
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.init(new SecretKeySpec(salt, "HmacSHA256"));
|
||||
return mac.doFinal(inputKeyMaterial);
|
||||
} catch (NoSuchAlgorithmException e) {
|
||||
throw new AssertionError(e);
|
||||
} catch (InvalidKeyException e) {
|
||||
} catch (NoSuchAlgorithmException | InvalidKeyException e) {
|
||||
throw new AssertionError(e);
|
||||
}
|
||||
}
|
||||
@ -73,9 +71,7 @@ public abstract class HKDF {
|
||||
}
|
||||
|
||||
return results.toByteArray();
|
||||
} catch (NoSuchAlgorithmException e) {
|
||||
throw new AssertionError(e);
|
||||
} catch (InvalidKeyException e) {
|
||||
} catch (NoSuchAlgorithmException | InvalidKeyException e) {
|
||||
throw new AssertionError(e);
|
||||
}
|
||||
}
|
||||
|
@ -6,6 +6,8 @@
|
||||
|
||||
package org.session.libsignal.streams;
|
||||
|
||||
import static org.session.libsignal.crypto.CipherUtil.CIPHER_LOCK;
|
||||
|
||||
import org.session.libsignal.exceptions.InvalidMacException;
|
||||
import org.session.libsignal.exceptions.InvalidMessageException;
|
||||
import org.session.libsignal.utilities.Util;
|
||||
@ -92,19 +94,15 @@ public class AttachmentCipherInputStream extends FilterInputStream {
|
||||
byte[] iv = new byte[BLOCK_SIZE];
|
||||
readFully(iv);
|
||||
|
||||
this.cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
|
||||
this.cipher.init(Cipher.DECRYPT_MODE, new SecretKeySpec(cipherKey, "AES"), new IvParameterSpec(iv));
|
||||
synchronized (CIPHER_LOCK) {
|
||||
this.cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
|
||||
this.cipher.init(Cipher.DECRYPT_MODE, new SecretKeySpec(cipherKey, "AES"), new IvParameterSpec(iv));
|
||||
}
|
||||
|
||||
this.done = false;
|
||||
this.totalRead = 0;
|
||||
this.totalDataSize = totalDataSize;
|
||||
} catch (NoSuchAlgorithmException e) {
|
||||
throw new AssertionError(e);
|
||||
} catch (InvalidKeyException e) {
|
||||
throw new AssertionError(e);
|
||||
} catch (NoSuchPaddingException e) {
|
||||
throw new AssertionError(e);
|
||||
} catch (InvalidAlgorithmParameterException e) {
|
||||
} catch (NoSuchAlgorithmException | InvalidKeyException | NoSuchPaddingException | InvalidAlgorithmParameterException e) {
|
||||
throw new AssertionError(e);
|
||||
}
|
||||
}
|
||||
@ -141,15 +139,12 @@ public class AttachmentCipherInputStream extends FilterInputStream {
|
||||
|
||||
private int readFinal(byte[] buffer, int offset, int length) throws IOException {
|
||||
try {
|
||||
int flourish = cipher.doFinal(buffer, offset);
|
||||
|
||||
done = true;
|
||||
return flourish;
|
||||
} catch (IllegalBlockSizeException e) {
|
||||
throw new IOException(e);
|
||||
} catch (BadPaddingException e) {
|
||||
throw new IOException(e);
|
||||
} catch (ShortBufferException e) {
|
||||
synchronized (CIPHER_LOCK) {
|
||||
int flourish = cipher.doFinal(buffer, offset);
|
||||
done = true;
|
||||
return flourish;
|
||||
}
|
||||
} catch (IllegalBlockSizeException | ShortBufferException | BadPaddingException e) {
|
||||
throw new IOException(e);
|
||||
}
|
||||
}
|
||||
@ -234,9 +229,7 @@ public class AttachmentCipherInputStream extends FilterInputStream {
|
||||
throw new InvalidMacException("Digest doesn't match!");
|
||||
}
|
||||
|
||||
} catch (IOException e) {
|
||||
throw new InvalidMacException(e);
|
||||
} catch (ArithmeticException e) {
|
||||
} catch (IOException | ArithmeticException e) {
|
||||
throw new InvalidMacException(e);
|
||||
} catch (NoSuchAlgorithmException e) {
|
||||
throw new AssertionError(e);
|
||||
|
@ -6,6 +6,8 @@
|
||||
|
||||
package org.session.libsignal.streams;
|
||||
|
||||
import static org.session.libsignal.crypto.CipherUtil.CIPHER_LOCK;
|
||||
|
||||
import org.session.libsignal.utilities.Util;
|
||||
|
||||
import java.io.IOException;
|
||||
@ -68,16 +70,17 @@ public class AttachmentCipherOutputStream extends DigestingOutputStream {
|
||||
@Override
|
||||
public void flush() throws IOException {
|
||||
try {
|
||||
byte[] ciphertext = cipher.doFinal();
|
||||
byte[] ciphertext;
|
||||
synchronized (CIPHER_LOCK) {
|
||||
ciphertext = cipher.doFinal();
|
||||
}
|
||||
byte[] auth = mac.doFinal(ciphertext);
|
||||
|
||||
super.write(ciphertext);
|
||||
super.write(auth);
|
||||
|
||||
super.flush();
|
||||
} catch (IllegalBlockSizeException e) {
|
||||
throw new AssertionError(e);
|
||||
} catch (BadPaddingException e) {
|
||||
} catch (IllegalBlockSizeException | BadPaddingException e) {
|
||||
throw new AssertionError(e);
|
||||
}
|
||||
}
|
||||
@ -97,9 +100,7 @@ public class AttachmentCipherOutputStream extends DigestingOutputStream {
|
||||
private Cipher initializeCipher() {
|
||||
try {
|
||||
return Cipher.getInstance("AES/CBC/PKCS5Padding");
|
||||
} catch (NoSuchAlgorithmException e) {
|
||||
throw new AssertionError(e);
|
||||
} catch (NoSuchPaddingException e) {
|
||||
} catch (NoSuchAlgorithmException | NoSuchPaddingException e) {
|
||||
throw new AssertionError(e);
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +1,7 @@
|
||||
package org.session.libsignal.streams;
|
||||
|
||||
import static org.session.libsignal.crypto.CipherUtil.CIPHER_LOCK;
|
||||
|
||||
import org.session.libsignal.utilities.Util;
|
||||
|
||||
import java.io.FilterInputStream;
|
||||
@ -62,23 +64,23 @@ public class ProfileCipherInputStream extends FilterInputStream {
|
||||
byte[] ciphertext = new byte[outputLength / 2];
|
||||
int read = in.read(ciphertext, 0, ciphertext.length);
|
||||
|
||||
if (read == -1) {
|
||||
if (cipher.getOutputSize(0) > outputLength) {
|
||||
throw new AssertionError("Need: " + cipher.getOutputSize(0) + " but only have: " + outputLength);
|
||||
}
|
||||
synchronized (CIPHER_LOCK) {
|
||||
if (read == -1) {
|
||||
if (cipher.getOutputSize(0) > outputLength) {
|
||||
throw new AssertionError("Need: " + cipher.getOutputSize(0) + " but only have: " + outputLength);
|
||||
}
|
||||
|
||||
finished = true;
|
||||
return cipher.doFinal(output, outputOffset);
|
||||
} else {
|
||||
if (cipher.getOutputSize(read) > outputLength) {
|
||||
throw new AssertionError("Need: " + cipher.getOutputSize(read) + " but only have: " + outputLength);
|
||||
}
|
||||
finished = true;
|
||||
return cipher.doFinal(output, outputOffset);
|
||||
} else {
|
||||
if (cipher.getOutputSize(read) > 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) {
|
||||
throw new AssertionError(e);
|
||||
} catch(ShortBufferException e) {
|
||||
} catch (IllegalBlockSizeException | ShortBufferException e) {
|
||||
throw new AssertionError(e);
|
||||
} catch (BadPaddingException e) {
|
||||
throw new IOException(e);
|
||||
|
@ -1,5 +1,7 @@
|
||||
package org.session.libsignal.streams;
|
||||
|
||||
import static org.session.libsignal.crypto.CipherUtil.CIPHER_LOCK;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
import java.security.InvalidAlgorithmParameterException;
|
||||
@ -54,20 +56,24 @@ public class ProfileCipherOutputStream extends DigestingOutputStream {
|
||||
byte[] input = new byte[1];
|
||||
input[0] = (byte)b;
|
||||
|
||||
byte[] output = cipher.update(input);
|
||||
byte[] output;
|
||||
synchronized (CIPHER_LOCK) {
|
||||
output = cipher.update(input);
|
||||
}
|
||||
super.write(output);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void flush() throws IOException {
|
||||
try {
|
||||
byte[] output = cipher.doFinal();
|
||||
byte[] output;
|
||||
synchronized (CIPHER_LOCK) {
|
||||
output = cipher.doFinal();
|
||||
}
|
||||
|
||||
super.write(output);
|
||||
super.flush();
|
||||
} catch (BadPaddingException e) {
|
||||
throw new AssertionError(e);
|
||||
} catch (IllegalBlockSizeException e) {
|
||||
} catch (BadPaddingException | IllegalBlockSizeException e) {
|
||||
throw new AssertionError(e);
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user