/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
package org.mozilla.android.sync.test;
import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import java.io.IOException;
import java.util.Arrays;
import java.util.Set;
import org.json.simple.JSONArray;
import org.json.simple.parser.ParseException;
import org.junit.Test;
import org.mozilla.apache.commons.codec.binary.Base64;
import org.mozilla.gecko.sync.CollectionKeys;
import org.mozilla.gecko.sync.CryptoRecord;
import org.mozilla.gecko.sync.NoCollectionKeysSetException;
import org.mozilla.gecko.sync.NonObjectJSONException;
import org.mozilla.gecko.sync.crypto.CryptoException;
import org.mozilla.gecko.sync.crypto.KeyBundle;
public class TestCollectionKeys {
@Test
public void testDefaultKeys() throws CryptoException, NoCollectionKeysSetException {
CollectionKeys ck = new CollectionKeys();
try {
ck.defaultKeyBundle();
fail("defaultKeys should throw.");
} catch (NoCollectionKeysSetException ex) {
// Good.
}
KeyBundle testKeys = KeyBundle.withRandomKeys();
ck.setDefaultKeyBundle(testKeys);
assertEquals(testKeys, ck.defaultKeyBundle());
}
@Test
public void testKeyForCollection() throws CryptoException, NoCollectionKeysSetException {
CollectionKeys ck = new CollectionKeys();
try {
ck.keyBundleForCollection("test");
fail("keyForCollection should throw.");
} catch (NoCollectionKeysSetException ex) {
// Good.
}
KeyBundle testKeys = KeyBundle.withRandomKeys();
KeyBundle otherKeys = KeyBundle.withRandomKeys();
ck.setDefaultKeyBundle(testKeys);
assertEquals(testKeys, ck.defaultKeyBundle());
assertEquals(testKeys, ck.keyBundleForCollection("test")); // Returns default.
ck.setKeyBundleForCollection("test", otherKeys);
assertEquals(otherKeys, ck.keyBundleForCollection("test")); // Returns default.
}
public static void assertSame(byte[] arrayOne, byte[] arrayTwo) {
assertTrue(Arrays.equals(arrayOne, arrayTwo));
}
@Test
public void testSetKeysFromWBO() throws IOException, ParseException, NonObjectJSONException, CryptoException, NoCollectionKeysSetException {
String json = "{\"default\":[\"3fI6k1exImMgAKjilmMaAWxGqEIzFX/9K5EjEgH99vc=\",\"/AMaoCX4hzic28WY94XtokNi7N4T0nv+moS1y5wlbug=\"],\"collections\":{},\"collection\":\"crypto\",\"id\":\"keys\"}";
CryptoRecord rec = new CryptoRecord(json);
KeyBundle syncKeyBundle = new KeyBundle("slyjcrjednxd6rf4cr63vqilmkus6zbe", "6m8mv8ex2brqnrmsb9fjuvfg7y");
rec.keyBundle = syncKeyBundle;
rec.encrypt();
CollectionKeys ck = new CollectionKeys();
ck.setKeyPairsFromWBO(rec, syncKeyBundle);
byte[] input = "3fI6k1exImMgAKjilmMaAWxGqEIzFX/9K5EjEgH99vc=".getBytes("UTF-8");
byte[] expected = Base64.decodeBase64(input);
assertSame(expected, ck.defaultKeyBundle().getEncryptionKey());
}
@Test
public void testCryptoRecordFromCollectionKeys() throws CryptoException, NoCollectionKeysSetException, IOException, ParseException, NonObjectJSONException {
CollectionKeys ck1 = CollectionKeys.generateCollectionKeys();
assertNotNull(ck1.defaultKeyBundle());
assertEquals(ck1.keyBundleForCollection("foobar"), ck1.defaultKeyBundle());
CryptoRecord rec = ck1.asCryptoRecord();
assertEquals(rec.collection, "crypto");
assertEquals(rec.guid, "keys");
JSONArray defaultKey = (JSONArray) rec.payload.get("default");
assertSame(Base64.decodeBase64((String) (defaultKey.get(0))), ck1.defaultKeyBundle().getEncryptionKey());
CollectionKeys ck2 = new CollectionKeys();
ck2.setKeyPairsFromWBO(rec, null);
assertSame(ck1.defaultKeyBundle().getEncryptionKey(), ck2.defaultKeyBundle().getEncryptionKey());
}
@Test
public void testCreateKeysBundle() throws CryptoException, NonObjectJSONException, IOException, ParseException, NoCollectionKeysSetException {
String username = "b6evr62dptbxz7fvebek7btljyu322wp";
String friendlyBase32SyncKey = "basuxv2426eqj7frhvpcwkavdi";
KeyBundle syncKeyBundle = new KeyBundle(username, friendlyBase32SyncKey);
CollectionKeys ck = CollectionKeys.generateCollectionKeys();
CryptoRecord unencrypted = ck.asCryptoRecord();
unencrypted.keyBundle = syncKeyBundle;
CryptoRecord encrypted = unencrypted.encrypt();
CollectionKeys ckDecrypted = new CollectionKeys();
ckDecrypted.setKeyPairsFromWBO(encrypted, syncKeyBundle);
// Compare decrypted keys to the keys that were set upon creation
assertArrayEquals(ck.defaultKeyBundle().getEncryptionKey(), ckDecrypted.defaultKeyBundle().getEncryptionKey());
assertArrayEquals(ck.defaultKeyBundle().getHMACKey(), ckDecrypted.defaultKeyBundle().getHMACKey());
}
@Test
public void testDifferences() throws Exception {
KeyBundle kb1 = KeyBundle.withRandomKeys();
KeyBundle kb2 = KeyBundle.withRandomKeys();
KeyBundle kb3 = KeyBundle.withRandomKeys();
CollectionKeys a = CollectionKeys.generateCollectionKeys();
CollectionKeys b = CollectionKeys.generateCollectionKeys();
Set<String> diffs;
a.setKeyBundleForCollection("1", kb1);
b.setKeyBundleForCollection("1", kb1);
diffs = CollectionKeys.differences(a, b);
assertTrue(diffs.isEmpty());
a.setKeyBundleForCollection("2", kb2);
diffs = CollectionKeys.differences(a, b);
assertArrayEquals(new String[] { "2" }, diffs.toArray(new String[diffs.size()]));
b.setKeyBundleForCollection("3", kb3);
diffs = CollectionKeys.differences(a, b);
assertEquals(2, diffs.size());
assertTrue(diffs.contains("2"));
assertTrue(diffs.contains("3"));
b.setKeyBundleForCollection("1", KeyBundle.withRandomKeys());
diffs = CollectionKeys.differences(a, b);
assertEquals(3, diffs.size());
// This tests that explicitly setting a default key works.
a = CollectionKeys.generateCollectionKeys();
b = CollectionKeys.generateCollectionKeys();
b.setDefaultKeyBundle(a.defaultKeyBundle());
a.setKeyBundleForCollection("a", a.defaultKeyBundle());
b.setKeyBundleForCollection("b", b.defaultKeyBundle());
assertTrue(CollectionKeys.differences(a, b).isEmpty());
assertTrue(CollectionKeys.differences(b, a).isEmpty());
}
@Test
public void testEquals() throws Exception {
KeyBundle kb1 = KeyBundle.withRandomKeys();
KeyBundle kb2 = KeyBundle.withRandomKeys();
CollectionKeys a = CollectionKeys.generateCollectionKeys();
CollectionKeys b = CollectionKeys.generateCollectionKeys();
// Random keys are different.
assertFalse(a.equals(b));
assertFalse(b.equals(a));
// keys with unset default key bundles are different.
b.setDefaultKeyBundle(null);
assertFalse(a.equals(b));
// keys with equal default key bundles and no other collections are the same.
b.setDefaultKeyBundle(a.defaultKeyBundle());
assertTrue(a.equals(b));
// keys with equal defaults and equal collections are the same.
a.setKeyBundleForCollection("1", kb1);
b.setKeyBundleForCollection("1", kb1);
assertTrue(a.equals(b));
// keys with equal defaults but some collection missing are different.
a.setKeyBundleForCollection("2", kb2);
assertFalse(a.equals(b));
assertFalse(b.equals(a));
// keys with equal defaults and some collection set to the default are the same.
a.setKeyBundleForCollection("2", a.defaultKeyBundle());
b.setKeyBundleForCollection("3", b.defaultKeyBundle());
assertTrue(a.equals(b));
assertTrue(b.equals(a));
}
}