From 24741fcc22dabaef7757d723be355654bc70e1b9 Mon Sep 17 00:00:00 2001 From: andrew Date: Fri, 5 May 2023 12:10:46 +0930 Subject: [PATCH 1/2] Synchronize Cipher in KeystoreHelper --- .../securesms/crypto/KeyStoreHelper.java | 28 +++++++++---------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/app/src/main/java/org/thoughtcrime/securesms/crypto/KeyStoreHelper.java b/app/src/main/java/org/thoughtcrime/securesms/crypto/KeyStoreHelper.java index 43e986559..45ec9e207 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/crypto/KeyStoreHelper.java +++ b/app/src/main/java/org/thoughtcrime/securesms/crypto/KeyStoreHelper.java @@ -45,44 +45,46 @@ public final class KeyStoreHelper { private static final String ANDROID_KEY_STORE = "AndroidKeyStore"; private static final String KEY_ALIAS = "SignalSecret"; - @RequiresApi(Build.VERSION_CODES.M) + private static final Object lock = new Object(); + public static SealedData seal(@NonNull byte[] input) { SecretKey secretKey = getOrCreateKeyStoreEntry(); try { - Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding"); - cipher.init(Cipher.ENCRYPT_MODE, secretKey); + synchronized (lock) { + Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding"); + cipher.init(Cipher.ENCRYPT_MODE, secretKey); - byte[] iv = cipher.getIV(); - byte[] data = cipher.doFinal(input); + byte[] iv = cipher.getIV(); + byte[] data = cipher.doFinal(input); - return new SealedData(iv, data); + return new SealedData(iv, data); + } } catch (NoSuchAlgorithmException | NoSuchPaddingException | InvalidKeyException | IllegalBlockSizeException | BadPaddingException e) { throw new AssertionError(e); } } - @RequiresApi(Build.VERSION_CODES.M) public static byte[] unseal(@NonNull SealedData sealedData) { SecretKey secretKey = getKeyStoreEntry(); try { - Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding"); - cipher.init(Cipher.DECRYPT_MODE, secretKey, new GCMParameterSpec(128, sealedData.iv)); + synchronized (lock) { + Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding"); + cipher.init(Cipher.DECRYPT_MODE, secretKey, new GCMParameterSpec(128, sealedData.iv)); - return cipher.doFinal(sealedData.data); + return cipher.doFinal(sealedData.data); + } } catch (NoSuchAlgorithmException | NoSuchPaddingException | InvalidKeyException | InvalidAlgorithmParameterException | IllegalBlockSizeException | BadPaddingException e) { throw new AssertionError(e); } } - @RequiresApi(Build.VERSION_CODES.M) private static SecretKey getOrCreateKeyStoreEntry() { if (hasKeyStoreEntry()) return getKeyStoreEntry(); else return createKeyStoreEntry(); } - @RequiresApi(Build.VERSION_CODES.M) private static SecretKey createKeyStoreEntry() { try { KeyGenerator keyGenerator = KeyGenerator.getInstance(KeyProperties.KEY_ALGORITHM_AES, ANDROID_KEY_STORE); @@ -99,7 +101,6 @@ public final class KeyStoreHelper { } } - @RequiresApi(Build.VERSION_CODES.M) private static SecretKey getKeyStoreEntry() { KeyStore keyStore = getKeyStore(); @@ -137,7 +138,6 @@ public final class KeyStoreHelper { } } - @RequiresApi(Build.VERSION_CODES.M) private static boolean hasKeyStoreEntry() { try { KeyStore ks = KeyStore.getInstance(ANDROID_KEY_STORE); From a152250a6007c423bda465f8c8824f1be5b73780 Mon Sep 17 00:00:00 2001 From: andrew Date: Fri, 5 May 2023 12:18:39 +0930 Subject: [PATCH 2/2] Add comments --- .../org/thoughtcrime/securesms/crypto/KeyStoreHelper.java | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/app/src/main/java/org/thoughtcrime/securesms/crypto/KeyStoreHelper.java b/app/src/main/java/org/thoughtcrime/securesms/crypto/KeyStoreHelper.java index 45ec9e207..001b4fa1a 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/crypto/KeyStoreHelper.java +++ b/app/src/main/java/org/thoughtcrime/securesms/crypto/KeyStoreHelper.java @@ -51,6 +51,9 @@ public final class KeyStoreHelper { SecretKey secretKey = getOrCreateKeyStoreEntry(); try { + // 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) { Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding"); cipher.init(Cipher.ENCRYPT_MODE, secretKey); @@ -69,6 +72,9 @@ public final class KeyStoreHelper { SecretKey secretKey = getKeyStoreEntry(); try { + // 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) { Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding"); cipher.init(Cipher.DECRYPT_MODE, secretKey, new GCMParameterSpec(128, sealedData.iv));