package uk.ac.cam.db538.cryptosms.storage; import java.io.IOException; import java.util.ArrayList; import uk.ac.cam.db538.cryptosms.storage.Conversation; import uk.ac.cam.db538.cryptosms.storage.Empty; import uk.ac.cam.db538.cryptosms.storage.Header; import uk.ac.cam.db538.cryptosms.storage.MessageData; import uk.ac.cam.db538.cryptosms.storage.MessageDataPart; import uk.ac.cam.db538.cryptosms.storage.SessionKeys; import uk.ac.cam.db538.cryptosms.storage.Storage; import uk.ac.cam.db538.cryptosms.storage.StorageFileException; import uk.ac.cam.db538.cryptosms.utils.Charset; import uk.ac.cam.db538.cryptosms.crypto.Encryption; import uk.ac.cam.db538.cryptosms.crypto.EncryptionNone; import uk.ac.cam.db538.cryptosms.crypto.EncryptionInterface.EncryptionException; import uk.ac.cam.db538.cryptosms.utils.LowLevel; import junit.framework.TestCase; public class Conversation_Test extends TestCase { // Testing data private String phoneNumber = "+447896512369"; private String phoneNumberLong = "+1234567890126549873sdfsat6ewrt987wet3df1g3s2g1e6r5t46wert4dfsgdfsg"; private String phoneNumberResult = "+1234567890126549873sdfsat6ewrt9"; private long indexSessionKeys = 122L; private long indexMessages = 120L; private long indexPrev = 12L; private long indexNext = 15L; private void setData(Conversation conv, boolean longPhoneNumber) { conv.setPhoneNumber((longPhoneNumber) ? phoneNumberLong : phoneNumber); conv.setIndexSessionKeys(indexSessionKeys); conv.setIndexMessages(indexMessages); conv.setIndexPrev(indexPrev); conv.setIndexNext(indexNext); } private void checkData(Conversation conv, boolean longPhoneNumber) { assertEquals(conv.getPhoneNumber(), (longPhoneNumber) ? phoneNumberResult : phoneNumber); assertEquals(conv.getIndexSessionKeys(), indexSessionKeys); assertEquals(conv.getIndexMessages(), indexMessages); assertEquals(conv.getIndexPrev(), indexPrev); assertEquals(conv.getIndexNext(), indexNext); } protected void setUp() throws Exception { super.setUp(); EncryptionNone.initEncryption(); Common.clearStorageFile(); } protected void tearDown() throws Exception { super.tearDown(); Common.closeStorageFile(); } public void testConstruction() throws StorageFileException, IOException { Conversation conv = Conversation.createConversation(); assertTrue(Common.checkStructure()); setData(conv, false); conv.saveToFile(); long index = conv.getEntryIndex(); // force it to be re-read from file Conversation.forceClearCache(); conv = Conversation.getConversation(index); checkData(conv, false); } public void testDelete() throws StorageFileException, IOException { for (int i = 0; i < 5; i++) { Conversation conv = Conversation.createConversation(); for (int j = 0; j < 10; ++j) { MessageData msg = MessageData.createMessageData(conv); { MessageDataPart part1 = MessageDataPart.createMessageDataPart(); MessageDataPart part2 = MessageDataPart.createMessageDataPart(); MessageDataPart part3 = MessageDataPart.createMessageDataPart(); MessageDataPart part4 = MessageDataPart.createMessageDataPart(); MessageDataPart part5 = MessageDataPart.createMessageDataPart(); ArrayList<MessageDataPart> list1 = new ArrayList<MessageDataPart>(); ArrayList<MessageDataPart> list2 = new ArrayList<MessageDataPart>(); list1.add(part1); list1.add(part2); list1.add(part3); msg.assignMessageDataParts(list1); assertSame(msg.getFirstMessageDataPart(), part1); list2.add(part4); list2.add(part5); msg.assignMessageDataParts(list2); assertSame(msg.getFirstMessageDataPart(), part4); } } for (int k = 0; k < 4; ++k) SessionKeys.createSessionKeys(conv); } assertTrue(Common.checkStructure()); Conversation conv; while ((conv = Header.getHeader().getFirstConversation()) != null) { conv.delete(); assertTrue(Common.checkStructure()); } } public void testIndices() throws StorageFileException, IOException { // INDEX OUT OF BOUNDS Conversation conv = Conversation.createConversation(); // indexSessionKeys try { conv.setIndexSessionKeys(0x100000000L); assertTrue(false); } catch (IndexOutOfBoundsException ex) { } try { conv.setIndexSessionKeys(0L); } catch (IndexOutOfBoundsException ex) { assertTrue(false); } try { conv.setIndexSessionKeys(-1L); assertTrue(false); } catch (IndexOutOfBoundsException ex) { } // indexMessages try { conv.setIndexMessages(0x100000000L); assertTrue(false); } catch (IndexOutOfBoundsException ex) { } try { conv.setIndexMessages(0L); } catch (IndexOutOfBoundsException ex) { assertTrue(false); } try { conv.setIndexMessages(-1L); assertTrue(false); } catch (IndexOutOfBoundsException ex) { } // indexPrev try { conv.setIndexPrev(0x100000000L); assertTrue(false); } catch (IndexOutOfBoundsException ex) { } try { conv.setIndexPrev(0L); } catch (IndexOutOfBoundsException ex) { assertTrue(false); } try { conv.setIndexPrev(-1L); assertTrue(false); } catch (IndexOutOfBoundsException ex) { } // indexNext try { conv.setIndexNext(0x100000000L); assertTrue(false); } catch (IndexOutOfBoundsException ex) { } try { conv.setIndexNext(0L); } catch (IndexOutOfBoundsException ex) { assertTrue(false); } try { conv.setIndexNext(-1L); assertTrue(false); } catch (IndexOutOfBoundsException ex) { } } public void testCreateData() throws StorageFileException, IOException, EncryptionException { byte flags = 0; Conversation conv = Conversation.createConversation() ; setData(conv, true); conv.saveToFile(); // get the generated data byte[] dataEncrypted = Storage.getStorage().getEntry(conv.getEntryIndex()); // chunk length assertEquals(dataEncrypted.length, Storage.CHUNK_SIZE); // decrypt the encoded part byte[] dataPlain = Encryption.getEncryption().decryptSymmetricWithMasterKey(dataEncrypted); // check the data assertEquals(flags, dataPlain[0]); assertEquals(phoneNumberResult, Charset.fromAscii8(dataPlain, 1, 32)); assertEquals(LowLevel.getUnsignedInt(dataPlain, Storage.ENCRYPTED_ENTRY_SIZE - 16), indexSessionKeys); assertEquals(LowLevel.getUnsignedInt(dataPlain, Storage.ENCRYPTED_ENTRY_SIZE - 12), indexMessages); assertEquals(LowLevel.getUnsignedInt(dataPlain, Storage.ENCRYPTED_ENTRY_SIZE - 8), indexPrev); assertEquals(LowLevel.getUnsignedInt(dataPlain, Storage.ENCRYPTED_ENTRY_SIZE - 4), indexNext); } public void testParseData() throws StorageFileException, IOException, EncryptionException { byte flags = 0; Conversation conv = Conversation.createConversation(); long index = conv.getEntryIndex(); // create plain data byte[] dataPlain = new byte[Storage.ENCRYPTED_ENTRY_SIZE]; dataPlain[0] = flags; System.arraycopy(Charset.toAscii8(phoneNumber, 32), 0, dataPlain, 1, 32); System.arraycopy(LowLevel.getBytesUnsignedInt(indexSessionKeys), 0, dataPlain, Storage.ENCRYPTED_ENTRY_SIZE - 16, 4); System.arraycopy(LowLevel.getBytesUnsignedInt(indexMessages), 0, dataPlain, Storage.ENCRYPTED_ENTRY_SIZE - 12, 4); System.arraycopy(LowLevel.getBytesUnsignedInt(indexPrev), 0, dataPlain, Storage.ENCRYPTED_ENTRY_SIZE - 8, 4); System.arraycopy(LowLevel.getBytesUnsignedInt(indexNext), 0, dataPlain, Storage.ENCRYPTED_ENTRY_SIZE - 4, 4); // encrypt it and inject it into the file byte[] dataEncrypted = Encryption.getEncryption().encryptSymmetricWithMasterKey(dataPlain); Storage.getStorage().setEntry(index, dataEncrypted); // have it parsed Conversation.forceClearCache(); conv = Conversation.getConversation(index); // check the indices checkData(conv, false); } public void testCreateConversation() throws StorageFileException, IOException { // check that it takes only one entry int countEmpty = Empty.getEmptyEntriesCount(); for (int i = 0; i < Storage.ALIGN_SIZE / Storage.CHUNK_SIZE * 5; ++i) { Conversation.createConversation(); if (countEmpty == 0) assertEquals(Storage.ALIGN_SIZE / Storage.CHUNK_SIZE - 1, (countEmpty = Empty.getEmptyEntriesCount())); else assertEquals(countEmpty - 1, (countEmpty = Empty.getEmptyEntriesCount())); } // check structure assertTrue(Common.checkStructure()); } }