package freenet.crypt; import static org.junit.Assert.assertArrayEquals; import static org.junit.Assert.assertEquals; 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.InputStream; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.OutputStream; import java.security.GeneralSecurityException; import java.security.Security; import java.util.Arrays; import java.util.Random; import org.bouncycastle.jce.provider.BouncyCastleProvider; import org.junit.After; import org.junit.Before; import org.junit.Test; import freenet.client.async.ClientContext; import freenet.support.api.Bucket; import freenet.support.api.LockableRandomAccessBuffer; import freenet.support.api.RandomAccessBucket; import freenet.support.io.ArrayBucket; import freenet.support.io.BucketTestBase; import freenet.support.io.BucketTools; import freenet.support.io.FileBucket; import freenet.support.io.FileUtil; import freenet.support.io.RAFBucket; import freenet.support.io.FileRandomAccessBuffer; import freenet.support.io.RandomAccessBufferTestBase; import freenet.support.io.ResumeFailedException; import freenet.support.io.StorageFormatException; public class EncryptedRandomAccessBucketTest extends BucketTestBase { private final static MasterSecret secret = new MasterSecret(); private final static EncryptedRandomAccessBufferType[] types = EncryptedRandomAccessBufferType.values(); static{ Security.addProvider(new BouncyCastleProvider()); } @Override protected Bucket makeBucket(long size) throws IOException { ArrayBucket underlying = new ArrayBucket(); return new EncryptedRandomAccessBucket(types[0], underlying, secret); } @Override protected void freeBucket(Bucket bucket) throws IOException { bucket.free(); } public void testIrregularWrites() throws IOException { Random r = new Random(6032405); int length = 1024*64+1; byte[] data = new byte[length]; RandomAccessBucket bucket = (RandomAccessBucket) makeBucket(length); OutputStream os = bucket.getOutputStream(); r.nextBytes(data); for(int written=0;written<length;) { int toWrite = Math.min(length - written, 4095); os.write(data, written, toWrite); written += toWrite; } os.close(); InputStream is = bucket.getInputStream(); for(int moved=0;moved<length;) { int readBytes = Math.min(length - moved, 4095); byte[] buf = new byte[readBytes]; readBytes = is.read(buf); assertTrue(readBytes > 0); assertTrue(Arrays.equals(Arrays.copyOfRange(buf, 0, readBytes), Arrays.copyOfRange(data, moved, moved+readBytes))); moved += readBytes; } is.close(); bucket.free(); } public void testIrregularWritesNotOverlapping() throws IOException { Random r = new Random(6032405); int length = 1024*64+1; byte[] data = new byte[length]; RandomAccessBucket bucket = (RandomAccessBucket) makeBucket(length); OutputStream os = bucket.getOutputStream(); r.nextBytes(data); for(int written=0;written<length;) { int toWrite = Math.min(length - written, 4095); os.write(data, written, toWrite); written += toWrite; } os.close(); InputStream is = bucket.getInputStream(); for(int moved=0;moved<length;) { int readBytes = Math.min(length - moved, 4093); // Co-prime with 4095 byte[] buf = new byte[readBytes]; readBytes = is.read(buf); assertTrue(readBytes > 0); assertTrue(Arrays.equals(Arrays.copyOfRange(buf, 0, readBytes), Arrays.copyOfRange(data, moved, moved+readBytes))); moved += readBytes; } is.close(); bucket.free(); } public void testBucketToRAF() throws IOException { Random r = new Random(6032405); int length = 1024*64+1; byte[] data = new byte[length]; RandomAccessBucket bucket = (RandomAccessBucket) makeBucket(length); OutputStream os = bucket.getOutputStream(); r.nextBytes(data); for(int written=0;written<length;) { int toWrite = Math.min(length - written, 4095); os.write(data, written, toWrite); written += toWrite; } os.close(); InputStream is = bucket.getInputStream(); for(int moved=0;moved<length;) { int readBytes = Math.min(length - moved, 4095); byte[] buf = new byte[readBytes]; readBytes = is.read(buf); assertTrue(readBytes > 0); assertTrue(Arrays.equals(Arrays.copyOfRange(buf, 0, readBytes), Arrays.copyOfRange(data, moved, moved+readBytes))); moved += readBytes; } LockableRandomAccessBuffer raf = bucket.toRandomAccessBuffer(); assertEquals(length, raf.size()); RAFBucket wrapped = new RAFBucket(raf); assertTrue(BucketTools.equalBuckets(bucket, wrapped)); for(int i=0;i<100;i++) { int end = length == 1 ? 1 : r.nextInt(length)+1; int start = r.nextInt(end); RandomAccessBufferTestBase.checkArraySectionEqualsReadData(data, raf, start, end, true); } } 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); FileBucket fb = new FileBucket(tempFile, false, false, false, true); EncryptedRandomAccessBucket erab = new EncryptedRandomAccessBucket(types[0], fb, secret); OutputStream os = erab.getOutputStream(); os.write(buf, 0, buf.length); os.close(); InputStream is = erab.getInputStream(); byte[] tmp = new byte[buf.length]; is.read(tmp, 0, buf.length); is.close(); assertArrayEquals(buf, tmp); ByteArrayOutputStream baos = new ByteArrayOutputStream(); DataOutputStream dos = new DataOutputStream(baos); erab.storeTo(dos); dos.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); EncryptedRandomAccessBucket restored = (EncryptedRandomAccessBucket) BucketTools.restoreFrom(dis, context.persistentFG, context.persistentFileTracker, secret); assertEquals(buf.length, restored.size()); assertEquals(erab, restored); tmp = new byte[buf.length]; is = erab.getInputStream(); is.read(tmp, 0, buf.length); assertArrayEquals(buf, tmp); is.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); FileBucket fb = new FileBucket(tempFile, false, false, false, true); EncryptedRandomAccessBucket erab = new EncryptedRandomAccessBucket(types[0], fb, secret); OutputStream os = erab.getOutputStream(); os.write(buf, 0, buf.length); os.close(); InputStream is = erab.getInputStream(); byte[] tmp = new byte[buf.length]; is.read(tmp, 0, buf.length); is.close(); assertArrayEquals(buf, tmp); ByteArrayOutputStream baos = new ByteArrayOutputStream(); ObjectOutputStream oos = new ObjectOutputStream(baos); oos.writeObject(erab); 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); EncryptedRandomAccessBucket restored = (EncryptedRandomAccessBucket) ois.readObject(); restored.onResume(context); assertEquals(buf.length, restored.size()); assertEquals(erab, restored); tmp = new byte[buf.length]; is = erab.getInputStream(); is.read(tmp, 0, buf.length); assertArrayEquals(buf, tmp); is.close(); restored.free(); } }