/* This code is part of Freenet. It is distributed under the GNU General * Public License, version 2 (or at your option any later version). See * http://www.gnu.org/ for further details of the GPL. */ package freenet.crypt; import static org.junit.Assert.*; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.DataInputStream; import java.io.DataOutputStream; import java.io.File; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.UnsupportedEncodingException; import java.nio.ByteBuffer; import java.security.GeneralSecurityException; import java.security.Security; import java.util.Random; import org.bouncycastle.jce.provider.BouncyCastleProvider; import org.junit.After; import org.junit.Before; import org.junit.Rule; import org.junit.Test; import org.junit.rules.ExpectedException; import freenet.client.async.ClientContext; import freenet.support.io.BucketTools; import freenet.support.io.ByteArrayRandomAccessBuffer; import freenet.support.io.FileUtil; import freenet.support.io.FileRandomAccessBuffer; import freenet.support.io.ResumeFailedException; import freenet.support.io.StorageFormatException; public class EncryptedRandomAccessBufferTest { private final static EncryptedRandomAccessBufferType[] types = EncryptedRandomAccessBufferType.values(); private final static byte[] message; static { try { message = "message".getBytes("UTF-8"); } catch (UnsupportedEncodingException e) { throw new Error(e); } } private final static MasterSecret secret = new MasterSecret(); private final static long falseMagic = 0x2c158a6c8882ffd3L; static{ Security.addProvider(new BouncyCastleProvider()); } @Rule public ExpectedException thrown= ExpectedException.none(); @Test public void testSuccesfulRoundTrip() throws IOException, GeneralSecurityException{ for(EncryptedRandomAccessBufferType type: types){ byte[] bytes = new byte[100]; ByteArrayRandomAccessBuffer barat = new ByteArrayRandomAccessBuffer(bytes); EncryptedRandomAccessBuffer erat = new EncryptedRandomAccessBuffer(type, barat, secret, true); erat.pwrite(0, message, 0, message.length); byte[] result = new byte[message.length]; erat.pread(0, result, 0, result.length); erat.close(); assertArrayEquals(message, result); } } @Test public void testSuccesfulRoundTripReadHeader() throws IOException, GeneralSecurityException{ for(EncryptedRandomAccessBufferType type: types){ byte[] bytes = new byte[100]; ByteArrayRandomAccessBuffer barat = new ByteArrayRandomAccessBuffer(bytes); EncryptedRandomAccessBuffer erat = new EncryptedRandomAccessBuffer(type, barat, secret, true); erat.pwrite(0, message, 0, message.length); erat.close(); ByteArrayRandomAccessBuffer barat2 = new ByteArrayRandomAccessBuffer(bytes); EncryptedRandomAccessBuffer erat2 = new EncryptedRandomAccessBuffer(type, barat2, secret, false); byte[] result = new byte[message.length]; erat2.pread(0, result, 0, result.length); erat2.close(); assertArrayEquals(message, result); } } @Test public void testWrongERATType() throws IOException, GeneralSecurityException { byte[] bytes = new byte[100]; ByteArrayRandomAccessBuffer barat = new ByteArrayRandomAccessBuffer(bytes); EncryptedRandomAccessBuffer erat = new EncryptedRandomAccessBuffer(types[0], barat, secret, true); erat.close(); ByteArrayRandomAccessBuffer barat2 = new ByteArrayRandomAccessBuffer(bytes); thrown.expect(IOException.class); thrown.expectMessage("This is not an EncryptedRandomAccessBuffer"); // Different header lengths. EncryptedRandomAccessBuffer erat2 = new EncryptedRandomAccessBuffer(types[1], barat2, secret, false); } @Test public void testUnderlyingRandomAccessThingTooSmall() throws GeneralSecurityException, IOException { byte[] bytes = new byte[10]; ByteArrayRandomAccessBuffer barat = new ByteArrayRandomAccessBuffer(bytes); thrown.expect(IOException.class); thrown.expectMessage("Underlying RandomAccessBuffer is not long enough to include the " + "footer."); EncryptedRandomAccessBuffer erat = new EncryptedRandomAccessBuffer(types[0], barat, secret, true); } @Test public void testWrongMagic() throws IOException, GeneralSecurityException{ byte[] bytes = new byte[100]; ByteArrayRandomAccessBuffer barat = new ByteArrayRandomAccessBuffer(bytes); EncryptedRandomAccessBuffer erat = new EncryptedRandomAccessBuffer(types[0], barat, secret, true); erat.close(); ByteArrayRandomAccessBuffer barat2 = new ByteArrayRandomAccessBuffer(bytes); byte[] magic = ByteBuffer.allocate(8).putLong(falseMagic).array(); barat2.pwrite(types[0].headerLen-8, magic, 0, 8); thrown.expect(IOException.class); thrown.expectMessage("This is not an EncryptedRandomAccessBuffer!"); EncryptedRandomAccessBuffer erat2 = new EncryptedRandomAccessBuffer(types[0], barat2, secret, false); } @Test public void testWrongMasterSecret() throws IOException, GeneralSecurityException{ byte[] bytes = new byte[100]; ByteArrayRandomAccessBuffer barat = new ByteArrayRandomAccessBuffer(bytes); EncryptedRandomAccessBuffer erat = new EncryptedRandomAccessBuffer(types[0], barat, secret, true); erat.close(); ByteArrayRandomAccessBuffer barat2 = new ByteArrayRandomAccessBuffer(bytes); thrown.expect(GeneralSecurityException.class); thrown.expectMessage("MAC is incorrect"); EncryptedRandomAccessBuffer erat2 = new EncryptedRandomAccessBuffer(types[0], barat2, new MasterSecret(), false); } @Test (expected = NullPointerException.class) public void testEncryptedRandomAccessThingNullInput1() throws GeneralSecurityException, IOException { byte[] bytes = new byte[10]; ByteArrayRandomAccessBuffer barat = new ByteArrayRandomAccessBuffer(bytes); EncryptedRandomAccessBuffer erat = new EncryptedRandomAccessBuffer(null, barat, secret, true); } @Test (expected = NullPointerException.class) public void testEncryptedRandomAccessThingNullByteArray() throws GeneralSecurityException, IOException { ByteArrayRandomAccessBuffer barat = new ByteArrayRandomAccessBuffer(null); EncryptedRandomAccessBuffer erat = new EncryptedRandomAccessBuffer(types[0], barat, secret, true); } @Test (expected = NullPointerException.class) public void testEncryptedRandomAccessThingNullBARAT() throws GeneralSecurityException, IOException { ByteArrayRandomAccessBuffer barat = null; EncryptedRandomAccessBuffer erat = new EncryptedRandomAccessBuffer(types[0], barat, secret, true); } @Test (expected = NullPointerException.class) public void testEncryptedRandomAccessThingNullInput3() throws GeneralSecurityException, IOException { byte[] bytes = new byte[10]; ByteArrayRandomAccessBuffer barat = new ByteArrayRandomAccessBuffer(bytes); EncryptedRandomAccessBuffer erat = new EncryptedRandomAccessBuffer(types[0], barat, null, true); } @Test public void testSize() throws IOException, GeneralSecurityException { byte[] bytes = new byte[100]; ByteArrayRandomAccessBuffer barat = new ByteArrayRandomAccessBuffer(bytes); EncryptedRandomAccessBuffer erat = new EncryptedRandomAccessBuffer(types[0], barat, secret, true); assertEquals(erat.size(), barat.size()-types[0].headerLen); } @Test public void testPreadFileOffsetTooSmall() throws IOException, GeneralSecurityException { byte[] bytes = new byte[100]; ByteArrayRandomAccessBuffer barat = new ByteArrayRandomAccessBuffer(bytes); EncryptedRandomAccessBuffer erat = new EncryptedRandomAccessBuffer(types[0], barat, secret, true); byte[] result = new byte[20]; thrown.expect(IllegalArgumentException.class); thrown.expectMessage("Cannot read before zero"); erat.pread(-1, result, 0, 20); } @Test public void testPreadFileOffsetTooBig() throws IOException, GeneralSecurityException { byte[] bytes = new byte[100]; ByteArrayRandomAccessBuffer barat = new ByteArrayRandomAccessBuffer(bytes); EncryptedRandomAccessBuffer erat = new EncryptedRandomAccessBuffer(types[0], barat, secret, true); int len = 20; byte[] result = new byte[len]; int offset = 100; thrown.expect(IOException.class); thrown.expectMessage("Cannot read after end: trying to read from "+offset+" to "+ (offset+len)+" on block length "+erat.size()); erat.pread(offset, result, 0, len); } @Test public void testPwriteFileOffsetTooSmall() throws IOException, GeneralSecurityException { byte[] bytes = new byte[100]; ByteArrayRandomAccessBuffer barat = new ByteArrayRandomAccessBuffer(bytes); EncryptedRandomAccessBuffer erat = new EncryptedRandomAccessBuffer(types[0], barat, secret, true); byte[] result = new byte[20]; thrown.expect(IllegalArgumentException.class); thrown.expectMessage("Cannot read before zero"); erat.pwrite(-1, result, 0, 20); } @Test public void testPwriteFileOffsetTooBig() throws IOException, GeneralSecurityException { byte[] bytes = new byte[100]; ByteArrayRandomAccessBuffer barat = new ByteArrayRandomAccessBuffer(bytes); EncryptedRandomAccessBuffer erat = new EncryptedRandomAccessBuffer(types[0], barat, secret, true); int len = 20; byte[] result = new byte[len]; int offset = 100; thrown.expect(IOException.class); thrown.expectMessage("Cannot write after end: trying to write from "+offset+" to "+ (offset+len)+" on block length "+erat.size()); erat.pwrite(offset, result, 0, len); } @Test public void testClose() throws IOException, GeneralSecurityException { byte[] bytes = new byte[100]; ByteArrayRandomAccessBuffer barat = new ByteArrayRandomAccessBuffer(bytes); EncryptedRandomAccessBuffer erat = new EncryptedRandomAccessBuffer(types[0], barat, secret, true); erat.close(); erat.close(); } @Test public void testClosePread() throws IOException, GeneralSecurityException { byte[] bytes = new byte[100]; ByteArrayRandomAccessBuffer barat = new ByteArrayRandomAccessBuffer(bytes); EncryptedRandomAccessBuffer erat = new EncryptedRandomAccessBuffer(types[0], barat, secret, true); erat.close(); byte[] result = new byte[20]; thrown.expect(IOException.class); thrown.expectMessage("This RandomAccessBuffer has already been closed. It can no longer" + " be read from."); erat.pread(0, result, 0, 20); } @Test public void testClosePwrite() throws IOException, GeneralSecurityException { byte[] bytes = new byte[100]; ByteArrayRandomAccessBuffer barat = new ByteArrayRandomAccessBuffer(bytes); EncryptedRandomAccessBuffer erat = new EncryptedRandomAccessBuffer(types[0], barat, secret, true); erat.close(); byte[] result = new byte[20]; thrown.expect(IOException.class); thrown.expectMessage("This RandomAccessBuffer has already been closed. It can no longer" + " be written to."); erat.pwrite(0, result, 0, 20); } private File base = new File("tmp.encrypted-random-access-thing-test"); @Before public void setUp() { base.mkdir(); } @After public void tearDown() { FileUtil.removeAll(base); } @Test public void testStoreTo() throws IOException, StorageFormatException, ResumeFailedException, GeneralSecurityException { File tempFile = File.createTempFile("test-storeto", ".tmp", base); byte[] buf = new byte[4096]; Random r = new Random(1267612); r.nextBytes(buf); FileRandomAccessBuffer rafw = new FileRandomAccessBuffer(tempFile, buf.length+types[0].headerLen, false); EncryptedRandomAccessBuffer eraf = new EncryptedRandomAccessBuffer(types[0], rafw, secret, true); eraf.pwrite(0, buf, 0, buf.length); byte[] tmp = new byte[buf.length]; eraf.pread(0, tmp, 0, buf.length); assertArrayEquals(buf, tmp); ByteArrayOutputStream baos = new ByteArrayOutputStream(); DataOutputStream dos = new DataOutputStream(baos); eraf.storeTo(dos); dos.close(); eraf.close(); DataInputStream dis = new DataInputStream(new ByteArrayInputStream(baos.toByteArray())); ClientContext context = new ClientContext(0, null, null, null, null, null, null, null, null, null, r, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null); context.setPersistentMasterSecret(secret); EncryptedRandomAccessBuffer restored = (EncryptedRandomAccessBuffer) BucketTools.restoreRAFFrom(dis, context.persistentFG, context.persistentFileTracker, secret); assertEquals(buf.length, restored.size()); //assertEquals(rafw, restored); tmp = new byte[buf.length]; restored.pread(0, tmp, 0, buf.length); assertArrayEquals(buf, tmp); restored.close(); restored.free(); } @Test public void testSerialize() throws IOException, StorageFormatException, ResumeFailedException, GeneralSecurityException, ClassNotFoundException { File tempFile = File.createTempFile("test-storeto", ".tmp", base); byte[] buf = new byte[4096]; Random r = new Random(1267612); r.nextBytes(buf); FileRandomAccessBuffer rafw = new FileRandomAccessBuffer(tempFile, buf.length+types[0].headerLen, false); EncryptedRandomAccessBuffer eraf = new EncryptedRandomAccessBuffer(types[0], rafw, secret, true); eraf.pwrite(0, buf, 0, buf.length); byte[] tmp = new byte[buf.length]; eraf.pread(0, tmp, 0, buf.length); assertArrayEquals(buf, tmp); ByteArrayOutputStream baos = new ByteArrayOutputStream(); ObjectOutputStream oos = new ObjectOutputStream(baos); oos.writeObject(eraf); oos.close(); DataInputStream dis = new DataInputStream(new ByteArrayInputStream(baos.toByteArray())); ClientContext context = new ClientContext(0, null, null, null, null, null, null, null, null, null, r, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null); context.setPersistentMasterSecret(secret); ObjectInputStream ois = new ObjectInputStream(dis); EncryptedRandomAccessBuffer restored = (EncryptedRandomAccessBuffer) ois.readObject(); restored.onResume(context); assertEquals(buf.length, restored.size()); assertEquals(eraf, restored); tmp = new byte[buf.length]; restored.pread(0, tmp, 0, buf.length); assertArrayEquals(buf, tmp); restored.close(); restored.free(); } }