mirror of
https://github.com/vanitasvitae/Smack.git
synced 2024-09-27 10:09:32 +02:00
1bce378e6d
Those exception are caused by I/O operations in the OmemoStore, which is now declaring that it throws those (since it is not uncommon for I/O operations to cause IOExceptions). After all, this is nicely demonstrated as this change is caused by switching with this commit to the Android API 19 compatible methods in FileBasedOmemoStore, which throw. The library can not decide what to do in case of those exceptions, hence it is sensible to expose them to the user.
353 lines
15 KiB
Java
353 lines
15 KiB
Java
/**
|
|
*
|
|
* Copyright Paul Schaub
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
package org.jivesoftware.smackx.omemo;
|
|
|
|
import static junit.framework.TestCase.assertEquals;
|
|
import static junit.framework.TestCase.assertFalse;
|
|
import static junit.framework.TestCase.assertNotNull;
|
|
import static junit.framework.TestCase.assertNotSame;
|
|
import static junit.framework.TestCase.assertNull;
|
|
import static junit.framework.TestCase.assertSame;
|
|
import static junit.framework.TestCase.assertTrue;
|
|
|
|
import java.io.IOException;
|
|
import java.util.Arrays;
|
|
import java.util.Date;
|
|
import java.util.HashMap;
|
|
import java.util.HashSet;
|
|
import java.util.TreeMap;
|
|
|
|
import org.jivesoftware.smackx.omemo.exceptions.CorruptedOmemoKeyException;
|
|
import org.jivesoftware.smackx.omemo.internal.OmemoCachedDeviceList;
|
|
import org.jivesoftware.smackx.omemo.internal.OmemoDevice;
|
|
import org.jivesoftware.smackx.omemo.trust.OmemoFingerprint;
|
|
|
|
import org.junit.AfterClass;
|
|
import org.junit.Test;
|
|
import org.junit.rules.TemporaryFolder;
|
|
import org.jxmpp.jid.impl.JidCreate;
|
|
import org.jxmpp.stringprep.XmppStringprepException;
|
|
|
|
|
|
public abstract class OmemoStoreTest<T_IdKeyPair, T_IdKey, T_PreKey, T_SigPreKey, T_Sess, T_Addr, T_ECPub, T_Bundle, T_Ciph> {
|
|
|
|
protected final OmemoStore<T_IdKeyPair, T_IdKey, T_PreKey, T_SigPreKey, T_Sess, T_Addr, T_ECPub, T_Bundle, T_Ciph> store;
|
|
private final OmemoDevice alice, bob;
|
|
|
|
private static final TemporaryFolder tmp = initStaticTemp();
|
|
|
|
OmemoStoreTest(OmemoStore<T_IdKeyPair, T_IdKey, T_PreKey, T_SigPreKey, T_Sess, T_Addr, T_ECPub, T_Bundle, T_Ciph> store)
|
|
throws XmppStringprepException {
|
|
this.store = store;
|
|
alice = new OmemoDevice(JidCreate.bareFrom("alice@wonderland.lit"), 123);
|
|
bob = new OmemoDevice(JidCreate.bareFrom("bob@builder.tv"), 987);
|
|
}
|
|
|
|
// Tests
|
|
|
|
@Test
|
|
public void keyUtilNotNull() {
|
|
assertNotNull(store.keyUtil());
|
|
}
|
|
|
|
@Test
|
|
public void generateOmemoIdentityKeyPairDoesNotReturnNull() {
|
|
assertNotNull(store.generateOmemoIdentityKeyPair());
|
|
}
|
|
|
|
@Test
|
|
public void identityKeyFromIdentityKeyPairIsNotNull() {
|
|
T_IdKeyPair pair = store.generateOmemoIdentityKeyPair();
|
|
assertNotNull(store.keyUtil().identityKeyFromPair(pair));
|
|
}
|
|
|
|
@Test
|
|
public void storeLoadRemoveOmemoIdentityKeyPair()
|
|
throws IOException, CorruptedOmemoKeyException {
|
|
|
|
T_IdKeyPair before = store.generateOmemoIdentityKeyPair();
|
|
|
|
assertNull(store.loadOmemoIdentityKeyPair(alice));
|
|
store.storeOmemoIdentityKeyPair(alice, before);
|
|
|
|
T_IdKeyPair after = store.loadOmemoIdentityKeyPair(alice);
|
|
assertNotNull(after);
|
|
|
|
// Fingerprints equal
|
|
assertEquals(store.keyUtil().getFingerprintOfIdentityKeyPair(before),
|
|
store.keyUtil().getFingerprintOfIdentityKeyPair(after));
|
|
|
|
// Byte-representation equals
|
|
assertTrue(Arrays.equals(
|
|
store.keyUtil().identityKeyPairToBytes(before),
|
|
store.keyUtil().identityKeyPairToBytes(after)));
|
|
|
|
// Non-existing keypair
|
|
assertNull("Must return null for non-existing key pairs.", store.loadOmemoIdentityKeyPair(bob));
|
|
|
|
// Deleting works
|
|
store.removeOmemoIdentityKeyPair(alice);
|
|
assertNull(store.loadOmemoIdentityKeyPair(alice));
|
|
}
|
|
|
|
@Test
|
|
public void storeLoadRemoveOmemoIdentityKey()
|
|
throws IOException, CorruptedOmemoKeyException {
|
|
|
|
// Create IdentityKeys and get bytes
|
|
T_IdKey keyA1 = store.keyUtil().identityKeyFromPair(store.generateOmemoIdentityKeyPair());
|
|
T_IdKey keyB1 = store.keyUtil().identityKeyFromPair(store.generateOmemoIdentityKeyPair());
|
|
byte[] bytesA1 = store.keyUtil().identityKeyToBytes(keyA1);
|
|
byte[] bytesB = store.keyUtil().identityKeyToBytes(keyB1);
|
|
|
|
// Not null and not of length 0
|
|
assertNotNull("Serialized identityKey cannot be null.", bytesA1);
|
|
assertNotNull("Serialized identityKey cannot be null.", bytesB);
|
|
assertNotSame("Serialized identityKey must be of length > 0.", 0, bytesA1.length);
|
|
assertNotSame("Serialized identityKey must be of length > 0.", 0, bytesB.length);
|
|
|
|
// Keys do not equal
|
|
assertFalse("Generated IdentityKeys must not be equal (ULTRA unlikely).",
|
|
Arrays.equals(bytesA1, bytesB));
|
|
|
|
// Loading must return null before and not null after saving
|
|
assertNull("Must return null, the store could not have this key by now.",
|
|
store.loadOmemoIdentityKey(alice, bob));
|
|
store.storeOmemoIdentityKey(alice, bob, keyA1);
|
|
T_IdKey keyA2 = store.loadOmemoIdentityKey(alice, bob);
|
|
assertNotNull(keyA2);
|
|
|
|
// Loaded key must equal stored one
|
|
byte[] bytesA2 = store.keyUtil().identityKeyToBytes(keyA2);
|
|
assertTrue("Serialized loaded key must equal serialized stored one.",
|
|
Arrays.equals(bytesA1, bytesA2));
|
|
|
|
// Non-existing keys must return null
|
|
assertNull("Non-existing keys must be returned as null.", store.loadOmemoIdentityKey(bob, alice));
|
|
|
|
// Key must vanish when deleted.
|
|
store.removeOmemoIdentityKey(alice, bob);
|
|
assertNull(store.loadOmemoIdentityKey(alice, bob));
|
|
}
|
|
|
|
@Test
|
|
public void generateOmemoPreKeys() {
|
|
TreeMap<Integer, T_PreKey> keys = store.generateOmemoPreKeys(31, 49);
|
|
assertNotNull("Generated data structure must not be null.", keys);
|
|
|
|
byte[] lastKey = null;
|
|
|
|
for (int i = 31; i <= 79; i++) {
|
|
assertEquals("Key ids must be ascending order, starting at 31.", Integer.valueOf(i), keys.firstKey());
|
|
assertNotNull("Every id must match to a key.", keys.get(keys.firstKey()));
|
|
byte[] bytes = store.keyUtil().preKeyToBytes(keys.get(keys.firstKey()));
|
|
assertNotNull("Serialized preKey must not be null.", bytes);
|
|
assertNotSame("Serialized preKey must not be of length 0.", 0, bytes.length);
|
|
|
|
if (lastKey != null) {
|
|
assertFalse("PreKeys MUST NOT be equal.", Arrays.equals(lastKey, bytes));
|
|
}
|
|
lastKey = bytes;
|
|
|
|
keys.remove(keys.firstKey());
|
|
|
|
}
|
|
|
|
assertEquals("After deleting 49 keys, there must be no keys left.", 0, keys.size());
|
|
}
|
|
|
|
@Test
|
|
public void storeLoadRemoveOmemoPreKeys()
|
|
throws IOException, InterruptedException {
|
|
|
|
TreeMap<Integer, T_PreKey> before = store.generateOmemoPreKeys(1, 10);
|
|
assertEquals("The store must have no prekeys before this test.", 0, store.loadOmemoPreKeys(alice).size());
|
|
|
|
store.storeOmemoPreKeys(alice, before);
|
|
TreeMap<Integer, T_PreKey> after = store.loadOmemoPreKeys(alice);
|
|
assertNotNull("Loaded preKeys must not be null.", after);
|
|
assertEquals("Loaded preKey count must equal stored count.", before.size(), after.size());
|
|
|
|
// Non-existing key must be returned as null
|
|
assertNull("Non-existing preKey must be returned as null.", store.loadOmemoPreKey(alice, 10000));
|
|
|
|
int last = after.size();
|
|
for (int i = 1; i <= last; i++) {
|
|
T_PreKey bKey = before.get(i);
|
|
T_PreKey aKey = after.get(i);
|
|
|
|
assertTrue("Loaded keys must equal stored ones.", Arrays.equals(
|
|
store.keyUtil().preKeyToBytes(bKey),
|
|
store.keyUtil().preKeyToBytes(aKey)));
|
|
|
|
T_PreKey rKey = store.loadOmemoPreKey(alice, i);
|
|
assertNotNull("Randomly accessed preKeys must not be null.", rKey);
|
|
assertTrue("Randomly accessed preKeys must equal the stored ones.", Arrays.equals(
|
|
store.keyUtil().preKeyToBytes(aKey),
|
|
store.keyUtil().preKeyToBytes(rKey)));
|
|
|
|
store.removeOmemoPreKey(alice, i);
|
|
assertNull("PreKey must be null after deletion.", store.loadOmemoPreKey(alice, i));
|
|
}
|
|
|
|
TreeMap<Integer, T_PreKey> postDeletion = store.loadOmemoPreKeys(alice);
|
|
assertSame("PreKey count must equal 0 after deletion of all keys.", 0, postDeletion.size());
|
|
}
|
|
|
|
@Test
|
|
public void storeLoadRemoveOmemoSignedPreKeys()
|
|
throws IOException, CorruptedOmemoKeyException {
|
|
|
|
TreeMap<Integer, T_SigPreKey> before = store.loadOmemoSignedPreKeys(alice);
|
|
assertEquals("At this stage, there must be no signed prekeys in the store.", 0, before.size());
|
|
|
|
T_IdKeyPair idp = store.generateOmemoIdentityKeyPair();
|
|
T_SigPreKey spk = store.generateOmemoSignedPreKey(idp, 125);
|
|
|
|
assertNotNull("SignedPreKey must not be null.", spk);
|
|
assertEquals("ID of signedPreKey must match.", 125, store.keyUtil().signedPreKeyIdFromKey(spk));
|
|
byte[] bytes = store.keyUtil().signedPreKeyToBytes(spk);
|
|
assertNotNull("Serialized signedPreKey must not be null", bytes);
|
|
assertNotSame("Serialized signedPreKey must not be of length 0.", 0, bytes.length);
|
|
|
|
// Stored key must equal loaded key
|
|
store.storeOmemoSignedPreKey(alice, 125, spk);
|
|
TreeMap<Integer, T_SigPreKey> after = store.loadOmemoSignedPreKeys(alice);
|
|
assertEquals("We must have exactly 1 signedPreKey now.", 1, after.size());
|
|
T_SigPreKey spk2 = after.get(after.firstKey());
|
|
assertEquals("Id of the stored signedPreKey must match the one we stored.",
|
|
125, store.keyUtil().signedPreKeyIdFromKey(spk2));
|
|
assertTrue("Serialization of stored and loaded signed preKey must equal.", Arrays.equals(
|
|
store.keyUtil().signedPreKeyToBytes(spk), store.keyUtil().signedPreKeyToBytes(spk2)));
|
|
|
|
// Random access
|
|
T_SigPreKey rspk = store.loadOmemoSignedPreKey(alice, 125);
|
|
assertTrue("Serialization of stored and randomly accessed signed preKey must equal.", Arrays.equals(
|
|
store.keyUtil().signedPreKeyToBytes(spk), store.keyUtil().signedPreKeyToBytes(rspk)));
|
|
assertNull("Non-existing signedPreKey must be returned as null.",
|
|
store.loadOmemoSignedPreKey(alice, 10000));
|
|
|
|
// Deleting
|
|
store.removeOmemoSignedPreKey(alice, 125);
|
|
assertNull("Deleted key must be returned as null.", store.loadOmemoSignedPreKey(alice, 125));
|
|
assertEquals(0, store.loadOmemoSignedPreKeys(alice).size());
|
|
}
|
|
|
|
@Test
|
|
public void loadStoreDateOfLastSignedPreKeyRenewal() throws IOException {
|
|
assertNull("The date of last signed preKey renewal must be null at this stage.",
|
|
store.getDateOfLastSignedPreKeyRenewal(alice));
|
|
Date before = new Date();
|
|
store.setDateOfLastSignedPreKeyRenewal(alice, before);
|
|
Date after = store.getDateOfLastSignedPreKeyRenewal(alice);
|
|
assertEquals("Dates must equal.", after, before);
|
|
}
|
|
|
|
@Test
|
|
public void loadStoreDateOfLastMessageReceived() throws IOException {
|
|
assertNull("The date of last message received must be null at this stage.",
|
|
store.getDateOfLastReceivedMessage(alice, bob));
|
|
Date before = new Date();
|
|
store.setDateOfLastReceivedMessage(alice, bob, before);
|
|
Date after = store.getDateOfLastReceivedMessage(alice, bob);
|
|
assertEquals("Dates must equal.", after, before);
|
|
}
|
|
|
|
@Test
|
|
public void loadStoreCachedDeviceList() throws IOException {
|
|
Integer[] active = new Integer[] {1, 5, 999, 10};
|
|
Integer[] inactive = new Integer[] {6, 7, 8};
|
|
OmemoCachedDeviceList before = new OmemoCachedDeviceList(
|
|
new HashSet<>(Arrays.asList(active)),
|
|
new HashSet<>(Arrays.asList(inactive)));
|
|
|
|
assertNotNull("Loading a non-existent cached deviceList must return an empty list.",
|
|
store.loadCachedDeviceList(alice, bob.getJid()));
|
|
|
|
store.storeCachedDeviceList(alice, bob.getJid(), before);
|
|
OmemoCachedDeviceList after = store.loadCachedDeviceList(alice, bob.getJid());
|
|
assertTrue("Loaded deviceList must not be empty", after.getAllDevices().size() != 0);
|
|
|
|
assertEquals("Number of entries in active devices must match.", active.length, after.getActiveDevices().size());
|
|
assertEquals("Number of entries in inactive devices must match.", inactive.length, after.getInactiveDevices().size());
|
|
assertEquals("Number of total entries must match.", active.length + inactive.length, after.getAllDevices().size());
|
|
|
|
for (Integer a : active) {
|
|
assertTrue(after.getActiveDevices().contains(a));
|
|
assertTrue(after.getAllDevices().contains(a));
|
|
}
|
|
|
|
for (Integer i : inactive) {
|
|
assertTrue(after.getInactiveDevices().contains(i));
|
|
assertTrue(after.getAllDevices().contains(i));
|
|
}
|
|
|
|
store.storeCachedDeviceList(alice, bob.getJid(), new OmemoCachedDeviceList());
|
|
assertEquals("DeviceList must be empty after overwriting it with empty list.", 0,
|
|
store.loadCachedDeviceList(alice, bob.getJid()).getAllDevices().size());
|
|
}
|
|
|
|
@Test
|
|
public void loadAllRawSessionsReturnsEmptyMapTest() throws IOException {
|
|
HashMap<Integer, T_Sess> sessions = store.loadAllRawSessionsOf(alice, bob.getJid());
|
|
assertNotNull(sessions);
|
|
assertEquals(0, sessions.size());
|
|
}
|
|
|
|
@Test
|
|
public void loadNonExistentRawSessionReturnsNullTest() throws IOException {
|
|
T_Sess session = store.loadRawSession(alice, bob);
|
|
assertNull(session);
|
|
}
|
|
|
|
@Test
|
|
public void loadStoreMessageCounterTest() throws IOException {
|
|
assertEquals(0, store.loadOmemoMessageCounter(alice, bob));
|
|
store.storeOmemoMessageCounter(alice, bob, 20);
|
|
assertEquals(20, store.loadOmemoMessageCounter(alice, bob));
|
|
}
|
|
|
|
@Test
|
|
public void getFingerprint() throws IOException, CorruptedOmemoKeyException {
|
|
assertNull("Method must return null for a non-existent fingerprint.", store.getFingerprint(alice));
|
|
store.storeOmemoIdentityKeyPair(alice, store.generateOmemoIdentityKeyPair());
|
|
OmemoFingerprint fingerprint = store.getFingerprint(alice);
|
|
assertNotNull("fingerprint must not be null", fingerprint);
|
|
assertEquals("Fingerprint must be of length 64", 64, fingerprint.length());
|
|
|
|
store.removeOmemoIdentityKeyPair(alice); //clean up
|
|
}
|
|
|
|
// ##############################################################
|
|
// Workaround for https://github.com/junit-team/junit4/issues/671
|
|
|
|
static TemporaryFolder initStaticTemp() {
|
|
try {
|
|
return new TemporaryFolder() { { before(); } };
|
|
} catch (Throwable t) {
|
|
throw new RuntimeException(t);
|
|
}
|
|
}
|
|
|
|
@AfterClass
|
|
public static void cleanup() throws Exception {
|
|
FileBasedOmemoStore.deleteDirectory(tmp.getRoot());
|
|
}
|
|
|
|
// ##############################################################
|
|
}
|