mirror of
https://github.com/signalapp/Signal-Android.git
synced 2026-07-13 17:23:56 +01:00
Convert SignalStorageCipher and keys to kotlin.
This commit is contained in:
-52
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
+69
@@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
-5
@@ -1,5 +0,0 @@
|
||||
package org.whispersystems.signalservice.api.storage;
|
||||
|
||||
public interface StorageCipherKey {
|
||||
byte[] serialize();
|
||||
}
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
package org.whispersystems.signalservice.api.storage
|
||||
|
||||
interface StorageCipherKey {
|
||||
fun serialize(): ByteArray
|
||||
}
|
||||
-38
@@ -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);
|
||||
}
|
||||
}
|
||||
+27
@@ -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()
|
||||
}
|
||||
}
|
||||
-56
@@ -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);
|
||||
}
|
||||
}
|
||||
+47
@@ -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()
|
||||
}
|
||||
}
|
||||
-38
@@ -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);
|
||||
}
|
||||
}
|
||||
+27
@@ -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()
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user