mirror of
https://github.com/signalapp/Signal-Android.git
synced 2026-04-25 19:29:54 +01:00
Reimplement ProfileCipherInputStream using libsignal-client.
libsignal-client provides an AES-GCM streaming interface that can replace the implementation in AES-GCM-Provider. Using it from ProfileCipherInputStream requires some knowledge about the tag size of AES-GCM, but frees it from the JCE interface. Note that it remains a serious error to not read the *entire* stream, since the authentication tag is at the end!
This commit is contained in:
committed by
Greyson Parrelli
parent
35e9e31a7b
commit
68a2d5ed20
@@ -1,9 +1,8 @@
|
||||
package org.whispersystems.signalservice.api.crypto;
|
||||
|
||||
|
||||
import junit.framework.TestCase;
|
||||
|
||||
import org.conscrypt.Conscrypt;
|
||||
import org.junit.Test;
|
||||
import org.signal.zkgroup.InvalidInputException;
|
||||
import org.signal.zkgroup.profiles.ProfileKey;
|
||||
import org.whispersystems.signalservice.internal.util.Util;
|
||||
@@ -11,14 +10,30 @@ import org.whispersystems.util.Base64;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.security.Security;
|
||||
|
||||
public class ProfileCipherTest extends TestCase {
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.fail;
|
||||
import static org.whispersystems.signalservice.testutil.LibSignalLibraryUtil.assumeLibSignalSupportedOnOS;
|
||||
|
||||
public class ProfileCipherTest {
|
||||
|
||||
private class TestByteArrayInputStream extends ByteArrayInputStream {
|
||||
TestByteArrayInputStream(byte[] buffer) {
|
||||
super(buffer);
|
||||
}
|
||||
|
||||
int getPos() {
|
||||
return this.pos;
|
||||
}
|
||||
}
|
||||
|
||||
static {
|
||||
Security.insertProviderAt(Conscrypt.newProvider(), 1);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testEncryptDecrypt() throws InvalidCiphertextException, InvalidInputException {
|
||||
ProfileKey key = new ProfileKey(Util.getSecretBytes(32));
|
||||
ProfileCipher cipher = new ProfileCipher(key);
|
||||
@@ -27,6 +42,7 @@ public class ProfileCipherTest extends TestCase {
|
||||
assertEquals(plaintext, "Clement\0Duval");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testEmpty() throws Exception {
|
||||
ProfileKey key = new ProfileKey(Util.getSecretBytes(32));
|
||||
ProfileCipher cipher = new ProfileCipher(key);
|
||||
@@ -36,7 +52,32 @@ public class ProfileCipherTest extends TestCase {
|
||||
assertEquals(plaintext.length(), 0);
|
||||
}
|
||||
|
||||
private byte[] readStream(byte[] input, ProfileKey key, int bufferSize) throws Exception {
|
||||
TestByteArrayInputStream bais = new TestByteArrayInputStream(input);
|
||||
assertEquals(0, bais.getPos());
|
||||
|
||||
ProfileCipherInputStream in = new ProfileCipherInputStream(bais, key);
|
||||
assertEquals(12 + 16, bais.getPos()); // initial read of nonce + tag-sized buffer
|
||||
|
||||
ByteArrayOutputStream result = new ByteArrayOutputStream();
|
||||
byte[] buffer = new byte[bufferSize];
|
||||
|
||||
int pos = bais.getPos();
|
||||
int read;
|
||||
while ((read = in.read(buffer)) != -1) {
|
||||
assertEquals(pos + read, bais.getPos());
|
||||
pos += read;
|
||||
result.write(buffer, 0, read);
|
||||
}
|
||||
|
||||
assertEquals(pos, input.length);
|
||||
return result.toByteArray();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testStreams() throws Exception {
|
||||
assumeLibSignalSupportedOnOS();
|
||||
|
||||
ProfileKey key = new ProfileKey(Util.getSecretBytes(32));
|
||||
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||
ProfileCipherOutputStream out = new ProfileCipherOutputStream(baos, key);
|
||||
@@ -45,21 +86,35 @@ public class ProfileCipherTest extends TestCase {
|
||||
out.flush();
|
||||
out.close();
|
||||
|
||||
ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
|
||||
ProfileCipherInputStream in = new ProfileCipherInputStream(bais, key);
|
||||
byte[] encrypted = baos.toByteArray();
|
||||
|
||||
ByteArrayOutputStream result = new ByteArrayOutputStream();
|
||||
byte[] buffer = new byte[2048];
|
||||
|
||||
int read;
|
||||
|
||||
while ((read = in.read(buffer)) != -1) {
|
||||
result.write(buffer, 0, read);
|
||||
}
|
||||
|
||||
assertEquals(new String(result.toByteArray()), "This is an avatar");
|
||||
assertEquals(new String(readStream(encrypted, key, 2048)), "This is an avatar");
|
||||
assertEquals(new String(readStream(encrypted, key, 16 /* == block size */)), "This is an avatar");
|
||||
assertEquals(new String(readStream(encrypted, key, 5)), "This is an avatar");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testStreamBadAuthentication() throws Exception {
|
||||
assumeLibSignalSupportedOnOS();
|
||||
|
||||
ProfileKey key = new ProfileKey(Util.getSecretBytes(32));
|
||||
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||
ProfileCipherOutputStream out = new ProfileCipherOutputStream(baos, key);
|
||||
|
||||
out.write("This is an avatar".getBytes());
|
||||
out.flush();
|
||||
out.close();
|
||||
|
||||
byte[] encrypted = baos.toByteArray();
|
||||
encrypted[encrypted.length - 1] ^= 1;
|
||||
try {
|
||||
readStream(encrypted, key, 2048);
|
||||
fail("failed to verify authenticate tag");
|
||||
} catch (IOException e) {
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testEncryptLengthBucket1() throws InvalidInputException {
|
||||
ProfileKey key = new ProfileKey(Util.getSecretBytes(32));
|
||||
ProfileCipher cipher = new ProfileCipher(key);
|
||||
@@ -70,6 +125,7 @@ public class ProfileCipherTest extends TestCase {
|
||||
assertEquals(108, encoded.length());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testEncryptLengthBucket2() throws InvalidInputException {
|
||||
ProfileKey key = new ProfileKey(Util.getSecretBytes(32));
|
||||
ProfileCipher cipher = new ProfileCipher(key);
|
||||
@@ -80,6 +136,7 @@ public class ProfileCipherTest extends TestCase {
|
||||
assertEquals(380, encoded.length());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testTargetNameLength() {
|
||||
assertEquals(53, ProfileCipher.getTargetNameLength("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"));
|
||||
assertEquals(53, ProfileCipher.getTargetNameLength("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1"));
|
||||
|
||||
Reference in New Issue
Block a user