From 8746f483c0e5d2f3e45b1869b0045bffc74ad366 Mon Sep 17 00:00:00 2001 From: Greyson Parrelli Date: Wed, 13 Nov 2024 10:26:59 -0500 Subject: [PATCH] Convert SignalStorageCipher and keys to kotlin. --- .../api/storage/SignalStorageCipher.java | 52 -------------- .../api/storage/SignalStorageCipher.kt | 69 +++++++++++++++++++ .../api/storage/StorageCipherKey.java | 5 -- .../api/storage/StorageCipherKey.kt | 5 ++ .../api/storage/StorageItemKey.java | 38 ---------- .../api/storage/StorageItemKey.kt | 27 ++++++++ .../signalservice/api/storage/StorageKey.java | 56 --------------- .../signalservice/api/storage/StorageKey.kt | 47 +++++++++++++ .../api/storage/StorageManifestKey.java | 38 ---------- .../api/storage/StorageManifestKey.kt | 27 ++++++++ 10 files changed, 175 insertions(+), 189 deletions(-) delete mode 100644 libsignal-service/src/main/java/org/whispersystems/signalservice/api/storage/SignalStorageCipher.java create mode 100644 libsignal-service/src/main/java/org/whispersystems/signalservice/api/storage/SignalStorageCipher.kt delete mode 100644 libsignal-service/src/main/java/org/whispersystems/signalservice/api/storage/StorageCipherKey.java create mode 100644 libsignal-service/src/main/java/org/whispersystems/signalservice/api/storage/StorageCipherKey.kt delete mode 100644 libsignal-service/src/main/java/org/whispersystems/signalservice/api/storage/StorageItemKey.java create mode 100644 libsignal-service/src/main/java/org/whispersystems/signalservice/api/storage/StorageItemKey.kt delete mode 100644 libsignal-service/src/main/java/org/whispersystems/signalservice/api/storage/StorageKey.java create mode 100644 libsignal-service/src/main/java/org/whispersystems/signalservice/api/storage/StorageKey.kt delete mode 100644 libsignal-service/src/main/java/org/whispersystems/signalservice/api/storage/StorageManifestKey.java create mode 100644 libsignal-service/src/main/java/org/whispersystems/signalservice/api/storage/StorageManifestKey.kt diff --git a/libsignal-service/src/main/java/org/whispersystems/signalservice/api/storage/SignalStorageCipher.java b/libsignal-service/src/main/java/org/whispersystems/signalservice/api/storage/SignalStorageCipher.java deleted file mode 100644 index f716f44d36..0000000000 --- a/libsignal-service/src/main/java/org/whispersystems/signalservice/api/storage/SignalStorageCipher.java +++ /dev/null @@ -1,52 +0,0 @@ -package org.whispersystems.signalservice.api.storage; - -import org.signal.libsignal.protocol.InvalidKeyException; -import org.whispersystems.signalservice.internal.util.Util; - -import java.security.InvalidAlgorithmParameterException; -import java.security.NoSuchAlgorithmException; - -import javax.crypto.BadPaddingException; -import javax.crypto.Cipher; -import javax.crypto.IllegalBlockSizeException; -import javax.crypto.NoSuchPaddingException; -import javax.crypto.spec.GCMParameterSpec; -import javax.crypto.spec.SecretKeySpec; - -/** - * Encrypts and decrypts data from the storage service. - */ -public class SignalStorageCipher { - - private static final int IV_LENGTH = 12; - - public static byte[] encrypt(StorageCipherKey key, byte[] data) { - try { - Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding"); - byte[] iv = Util.getSecretBytes(IV_LENGTH); - - cipher.init(Cipher.ENCRYPT_MODE, new SecretKeySpec(key.serialize(), "AES"), new GCMParameterSpec(128, iv)); - byte[] ciphertext = cipher.doFinal(data); - - return Util.join(iv, ciphertext); - } catch (NoSuchAlgorithmException | java.security.InvalidKeyException | InvalidAlgorithmParameterException | NoSuchPaddingException | BadPaddingException | IllegalBlockSizeException e) { - throw new AssertionError(e); - } - } - - public static byte[] decrypt(StorageCipherKey key, byte[] data) throws InvalidKeyException { - try { - Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding"); - byte[][] split = Util.split(data, IV_LENGTH, data.length - IV_LENGTH); - byte[] iv = split[0]; - byte[] cipherText = split[1]; - - cipher.init(Cipher.DECRYPT_MODE, new SecretKeySpec(key.serialize(), "AES"), new GCMParameterSpec(128, iv)); - return cipher.doFinal(cipherText); - } catch (java.security.InvalidKeyException | BadPaddingException | IllegalBlockSizeException e) { - throw new InvalidKeyException(e); - } catch (NoSuchAlgorithmException | NoSuchPaddingException | InvalidAlgorithmParameterException e) { - throw new AssertionError(e); - } - } -} diff --git a/libsignal-service/src/main/java/org/whispersystems/signalservice/api/storage/SignalStorageCipher.kt b/libsignal-service/src/main/java/org/whispersystems/signalservice/api/storage/SignalStorageCipher.kt new file mode 100644 index 0000000000..b2880b6dba --- /dev/null +++ b/libsignal-service/src/main/java/org/whispersystems/signalservice/api/storage/SignalStorageCipher.kt @@ -0,0 +1,69 @@ +package org.whispersystems.signalservice.api.storage + +import org.whispersystems.signalservice.internal.util.Util +import java.security.InvalidAlgorithmParameterException +import java.security.InvalidKeyException +import java.security.NoSuchAlgorithmException +import javax.crypto.BadPaddingException +import javax.crypto.Cipher +import javax.crypto.IllegalBlockSizeException +import javax.crypto.NoSuchPaddingException +import javax.crypto.spec.GCMParameterSpec +import javax.crypto.spec.SecretKeySpec + +/** + * Encrypts and decrypts data from the storage service. + */ +object SignalStorageCipher { + private const val IV_LENGTH = 12 + + @JvmStatic + fun encrypt(key: StorageCipherKey, data: ByteArray): ByteArray { + try { + val cipher = Cipher.getInstance("AES/GCM/NoPadding") + val iv = Util.getSecretBytes(IV_LENGTH) + + cipher.init(Cipher.ENCRYPT_MODE, SecretKeySpec(key.serialize(), "AES"), GCMParameterSpec(128, iv)) + val ciphertext = cipher.doFinal(data) + + return iv + ciphertext + } catch (e: NoSuchAlgorithmException) { + throw AssertionError(e) + } catch (e: InvalidKeyException) { + throw AssertionError(e) + } catch (e: InvalidAlgorithmParameterException) { + throw AssertionError(e) + } catch (e: NoSuchPaddingException) { + throw AssertionError(e) + } catch (e: BadPaddingException) { + throw AssertionError(e) + } catch (e: IllegalBlockSizeException) { + throw AssertionError(e) + } + } + + @JvmStatic + @Throws(org.signal.libsignal.protocol.InvalidKeyException::class) + fun decrypt(key: StorageCipherKey, data: ByteArray): ByteArray { + try { + val cipher = Cipher.getInstance("AES/GCM/NoPadding") + val iv = data.copyOfRange(0, IV_LENGTH) + val cipherText = data.copyOfRange(IV_LENGTH, data.size) + + cipher.init(Cipher.DECRYPT_MODE, SecretKeySpec(key.serialize(), "AES"), GCMParameterSpec(128, iv)) + return cipher.doFinal(cipherText) + } catch (e: InvalidKeyException) { + throw org.signal.libsignal.protocol.InvalidKeyException(e) + } catch (e: BadPaddingException) { + throw org.signal.libsignal.protocol.InvalidKeyException(e) + } catch (e: IllegalBlockSizeException) { + throw org.signal.libsignal.protocol.InvalidKeyException(e) + } catch (e: NoSuchAlgorithmException) { + throw AssertionError(e) + } catch (e: NoSuchPaddingException) { + throw AssertionError(e) + } catch (e: InvalidAlgorithmParameterException) { + throw AssertionError(e) + } + } +} diff --git a/libsignal-service/src/main/java/org/whispersystems/signalservice/api/storage/StorageCipherKey.java b/libsignal-service/src/main/java/org/whispersystems/signalservice/api/storage/StorageCipherKey.java deleted file mode 100644 index 474de1e6de..0000000000 --- a/libsignal-service/src/main/java/org/whispersystems/signalservice/api/storage/StorageCipherKey.java +++ /dev/null @@ -1,5 +0,0 @@ -package org.whispersystems.signalservice.api.storage; - -public interface StorageCipherKey { - byte[] serialize(); -} diff --git a/libsignal-service/src/main/java/org/whispersystems/signalservice/api/storage/StorageCipherKey.kt b/libsignal-service/src/main/java/org/whispersystems/signalservice/api/storage/StorageCipherKey.kt new file mode 100644 index 0000000000..1134716910 --- /dev/null +++ b/libsignal-service/src/main/java/org/whispersystems/signalservice/api/storage/StorageCipherKey.kt @@ -0,0 +1,5 @@ +package org.whispersystems.signalservice.api.storage + +interface StorageCipherKey { + fun serialize(): ByteArray +} diff --git a/libsignal-service/src/main/java/org/whispersystems/signalservice/api/storage/StorageItemKey.java b/libsignal-service/src/main/java/org/whispersystems/signalservice/api/storage/StorageItemKey.java deleted file mode 100644 index 81400b93d4..0000000000 --- a/libsignal-service/src/main/java/org/whispersystems/signalservice/api/storage/StorageItemKey.java +++ /dev/null @@ -1,38 +0,0 @@ -package org.whispersystems.signalservice.api.storage; - -import java.util.Arrays; - -/** - * Key used to encrypt individual storage items in the storage service. - * - * Created via {@link StorageKey#deriveItemKey(byte[]) }. - */ -public final class StorageItemKey implements StorageCipherKey { - - private static final int LENGTH = 32; - - private final byte[] key; - - StorageItemKey(byte[] key) { - if (key.length != LENGTH) throw new AssertionError(); - - this.key = key; - } - - @Override - public byte[] serialize() { - return key.clone(); - } - - @Override - public boolean equals(Object o) { - if (o == null || o.getClass() != getClass()) return false; - - return Arrays.equals(((StorageItemKey) o).key, key); - } - - @Override - public int hashCode() { - return Arrays.hashCode(key); - } -} diff --git a/libsignal-service/src/main/java/org/whispersystems/signalservice/api/storage/StorageItemKey.kt b/libsignal-service/src/main/java/org/whispersystems/signalservice/api/storage/StorageItemKey.kt new file mode 100644 index 0000000000..90a58702c0 --- /dev/null +++ b/libsignal-service/src/main/java/org/whispersystems/signalservice/api/storage/StorageItemKey.kt @@ -0,0 +1,27 @@ +package org.whispersystems.signalservice.api.storage + +/** + * Key used to encrypt individual storage items in the storage service. + * + * Created via [StorageKey.deriveItemKey]. + */ +class StorageItemKey(val key: ByteArray) : StorageCipherKey { + init { + check(key.size == 32) + } + + override fun serialize(): ByteArray = key.clone() + + override fun equals(other: Any?): Boolean { + if (this === other) return true + if (javaClass != other?.javaClass) return false + + other as StorageItemKey + + return key.contentEquals(other.key) + } + + override fun hashCode(): Int { + return key.contentHashCode() + } +} diff --git a/libsignal-service/src/main/java/org/whispersystems/signalservice/api/storage/StorageKey.java b/libsignal-service/src/main/java/org/whispersystems/signalservice/api/storage/StorageKey.java deleted file mode 100644 index 12bca1393b..0000000000 --- a/libsignal-service/src/main/java/org/whispersystems/signalservice/api/storage/StorageKey.java +++ /dev/null @@ -1,56 +0,0 @@ -package org.whispersystems.signalservice.api.storage; - -import org.whispersystems.signalservice.api.kbs.MasterKey; -import org.signal.core.util.Base64; -import org.whispersystems.util.StringUtil; - -import java.util.Arrays; - -import static org.signal.core.util.CryptoUtil.hmacSha256; - -/** - * Key used to encrypt data on the storage service. Not used directly -- instead we used keys that - * are derived for each item we're storing. - * - * Created via {@link MasterKey#deriveStorageServiceKey()}. - */ -public final class StorageKey { - - private static final int LENGTH = 32; - - private final byte[] key; - - public StorageKey(byte[] key) { - if (key.length != LENGTH) throw new AssertionError(); - - this.key = key; - } - - public StorageManifestKey deriveManifestKey(long version) { - return new StorageManifestKey(derive("Manifest_" + version)); - } - - public StorageItemKey deriveItemKey(byte[] key) { - return new StorageItemKey(derive("Item_" + Base64.encodeWithPadding(key))); - } - - private byte[] derive(String keyName) { - return hmacSha256(key, StringUtil.utf8(keyName)); - } - - public byte[] serialize() { - return key.clone(); - } - - @Override - public boolean equals(Object o) { - if (o == null || o.getClass() != getClass()) return false; - - return Arrays.equals(((StorageKey) o).key, key); - } - - @Override - public int hashCode() { - return Arrays.hashCode(key); - } -} diff --git a/libsignal-service/src/main/java/org/whispersystems/signalservice/api/storage/StorageKey.kt b/libsignal-service/src/main/java/org/whispersystems/signalservice/api/storage/StorageKey.kt new file mode 100644 index 0000000000..5a7ab67393 --- /dev/null +++ b/libsignal-service/src/main/java/org/whispersystems/signalservice/api/storage/StorageKey.kt @@ -0,0 +1,47 @@ +package org.whispersystems.signalservice.api.storage + +import org.signal.core.util.Base64.encodeWithPadding +import org.signal.core.util.CryptoUtil +import org.whispersystems.signalservice.api.kbs.MasterKey +import org.whispersystems.util.StringUtil + +/** + * Key used to encrypt data on the storage service. Not used directly -- instead we used keys that + * are derived for each item we're storing. + * + * Created via [MasterKey.deriveStorageServiceKey]. + */ +class StorageKey(val key: ByteArray) { + init { + check(key.size == 32) + } + + fun deriveManifestKey(version: Long): StorageManifestKey { + return StorageManifestKey(derive("Manifest_$version")) + } + + fun deriveItemKey(key: ByteArray): StorageItemKey { + return StorageItemKey(derive("Item_" + encodeWithPadding(key))) + } + + private fun derive(keyName: String): ByteArray { + return CryptoUtil.hmacSha256(key, StringUtil.utf8(keyName)) + } + + fun serialize(): ByteArray { + return key.clone() + } + + override fun equals(other: Any?): Boolean { + if (this === other) return true + if (javaClass != other?.javaClass) return false + + other as StorageKey + + return key.contentEquals(other.key) + } + + override fun hashCode(): Int { + return key.contentHashCode() + } +} diff --git a/libsignal-service/src/main/java/org/whispersystems/signalservice/api/storage/StorageManifestKey.java b/libsignal-service/src/main/java/org/whispersystems/signalservice/api/storage/StorageManifestKey.java deleted file mode 100644 index 98977b66fe..0000000000 --- a/libsignal-service/src/main/java/org/whispersystems/signalservice/api/storage/StorageManifestKey.java +++ /dev/null @@ -1,38 +0,0 @@ -package org.whispersystems.signalservice.api.storage; - -import java.util.Arrays; - -/** - * Key used to encrypt a manifest in the storage service. - * - * Created via {@link StorageKey#deriveManifestKey(long)}. - */ -public final class StorageManifestKey implements StorageCipherKey { - - private static final int LENGTH = 32; - - private final byte[] key; - - StorageManifestKey(byte[] key) { - if (key.length != LENGTH) throw new AssertionError(); - - this.key = key; - } - - @Override - public byte[] serialize() { - return key.clone(); - } - - @Override - public boolean equals(Object o) { - if (o == null || o.getClass() != getClass()) return false; - - return Arrays.equals(((StorageManifestKey) o).key, key); - } - - @Override - public int hashCode() { - return Arrays.hashCode(key); - } -} diff --git a/libsignal-service/src/main/java/org/whispersystems/signalservice/api/storage/StorageManifestKey.kt b/libsignal-service/src/main/java/org/whispersystems/signalservice/api/storage/StorageManifestKey.kt new file mode 100644 index 0000000000..2a5aaa9428 --- /dev/null +++ b/libsignal-service/src/main/java/org/whispersystems/signalservice/api/storage/StorageManifestKey.kt @@ -0,0 +1,27 @@ +package org.whispersystems.signalservice.api.storage + +/** + * Key used to encrypt a manifest in the storage service. + * + * Created via [StorageKey.deriveManifestKey]. + */ +class StorageManifestKey(val key: ByteArray) : StorageCipherKey { + init { + check(key.size == 32) + } + + override fun serialize(): ByteArray = key.clone() + + override fun equals(other: Any?): Boolean { + if (this === other) return true + if (javaClass != other?.javaClass) return false + + other as StorageManifestKey + + return key.contentEquals(other.key) + } + + override fun hashCode(): Int { + return key.contentHashCode() + } +}