package mobisocial.musubi.encoding; import java.nio.ByteBuffer; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.util.Date; import java.util.Random; import mobisocial.crypto.IBEncryptionScheme; import mobisocial.crypto.IBEncryptionScheme.ConversationKey; import mobisocial.crypto.IBEncryptionScheme.UserKey; import mobisocial.crypto.IBHashedIdentity.Authority; import mobisocial.crypto.IBIdentity; import mobisocial.crypto.IBSignatureScheme; import mobisocial.musubi.encoding.DiscardMessage.Corrupted; import mobisocial.musubi.encoding.NeedsKey.Signature; import mobisocial.musubi.identity.UnverifiedIdentityProvider; import mobisocial.musubi.model.MDevice; import mobisocial.musubi.model.MIdentity; import mobisocial.musubi.model.MIncomingSecret; import mobisocial.musubi.model.MOutgoingSecret; import mobisocial.musubi.model.helpers.DatabaseFile; import mobisocial.musubi.model.helpers.DeviceManager; import mobisocial.musubi.model.helpers.MessageTransportManager; import mobisocial.musubi.protocol.Message; import mobisocial.musubi.protocol.Recipient; import mobisocial.musubi.protocol.Secret; import mobisocial.musubi.util.Util; import android.database.sqlite.SQLiteOpenHelper; import android.util.Log; public class EncoderPerformanceTest extends TestBase { final static int R = 229; UnverifiedIdentityProvider mIdp = new UnverifiedIdentityProvider(); IBIdentity mMe = new IBIdentity(Authority.Email, randomUniquePrincipal(), 0); final static int ITERATIONS = 100; SQLiteOpenHelper dbh; public void setUp() { dbh = new DatabaseFile(getContext(), null); } public void tearDown() { dbh.close(); } public void testOutgoingLoadChannelKey() { DeviceManager dm = new DeviceManager(dbh); MessageTransportManager mtm = new MessageTransportManager(dbh, mIdp.getEncryptionScheme(), mIdp.getSignatureScheme(), dm.getLocalDeviceName()); MIdentity meIdentity = new MIdentity(); MDevice meDevice = new MDevice(); meDevice.id_ = 1; MOutgoingSecret os = new MOutgoingSecret(); os.otherIdentityId_ = 1; os.myIdentityId_ = 1; os.signatureWhen_ = mMe.temporalFrame_; os.encryptionWhen_ = mMe.temporalFrame_; os.key_ = new byte[32]; os.encryptedKey_ = new byte[42]; os.signature_ = new byte[21]; new Random().nextBytes(os.signature_); mtm.insertOutgoingSecret(mMe, mMe, os); //assume 1000 contacts for(int i = 0; i < 1000; ++i) { os.otherIdentityId_++; mtm.insertOutgoingSecret(mMe, mMe, os); } Date start = new Date(); for(int i = 0; i < ITERATIONS; ++i) { mtm.lookupOutgoingSecret(meIdentity, meIdentity, mMe, mMe); } Date end = new Date(); Log.w(this.getName(), "Milliseconds per lookup " + (double)(end.getTime() - start.getTime()) / (ITERATIONS)); } public void testIncomingLoadChannelKey() { DeviceManager dm = new DeviceManager(dbh); MessageTransportManager mtm = new MessageTransportManager(dbh, mIdp.getEncryptionScheme(), mIdp.getSignatureScheme(), dm.getLocalDeviceName()); MIdentity meIdentity = new MIdentity(); MDevice meDevice = new MDevice(); meDevice.id_ = 1; MIncomingSecret is = new MIncomingSecret(); is.deviceId_ = 1; is.otherIdentityId_ = 1; is.myIdentityId_ = 1; is.signatureWhen_ = mMe.temporalFrame_; is.encryptionWhen_ = mMe.temporalFrame_; is.key_ = new byte[32]; is.encryptedKey_ = new byte[42]; is.signature_ = new byte[21]; new Random().nextBytes(is.signature_); mtm.insertIncomingSecret(mMe, mMe, is); //assume 1000 contacts for(int i = 0; i < 1000; ++i) { is.otherIdentityId_++; mtm.insertIncomingSecret(mMe, mMe, is); } Date start = new Date(); for(int i = 0; i < ITERATIONS; ++i) { mtm.lookupIncomingSecret(meIdentity, meDevice, meIdentity, is.signature_, mMe, mMe); } Date end = new Date(); Log.w(this.getName(), "Milliseconds per lookup " + (double)(end.getTime() - start.getTime()) / (ITERATIONS)); } public void testComputeOutgoingKey() throws Signature { Date start = new Date(); IBEncryptionScheme enc = mIdp.getEncryptionScheme(); for(int i = 0; i < ITERATIONS / 10; ++i) { enc.randomConversationKey(mMe); } Date end = new Date(); Log.w(this.getName(), "Milliseconds per channel key " + (double)(end.getTime() - start.getTime()) / (ITERATIONS / 10)); } public void testSignOutgoingKey() throws Signature { TransientTransportDataProvider tdp = new TransientTransportDataProvider(mIdp.getEncryptionScheme(), mIdp.getSignatureScheme(), mMe, null, null, null); Date start = new Date(); IBSignatureScheme sig = mIdp.getSignatureScheme(); byte[] encrypted_key = new byte[32]; new Random().nextBytes(encrypted_key); long device_name = new Random().nextLong(); for(int i = 0; i < ITERATIONS / 10; ++i) { MessageDigest md; try { md = MessageDigest.getInstance("SHA-256"); } catch (NoSuchAlgorithmException e) { throw new RuntimeException("your platform does not support sha256", e); } md.update(encrypted_key); ByteBuffer bb = ByteBuffer.wrap(new byte[8]); bb.putLong(device_name); byte[] hash = md.digest(bb.array()); sig.sign(mMe, tdp.getSignatureKey(null, mMe), hash); } Date end = new Date(); Log.w(this.getName(), "Milliseconds per sign channel key " + (double)(end.getTime() - start.getTime()) / (ITERATIONS / 10)); } public void testVerifyChannelKey() throws Signature { TransientTransportDataProvider tdp = new TransientTransportDataProvider(mIdp.getEncryptionScheme(), mIdp.getSignatureScheme(), mMe, null, null, null); Date start = new Date(); IBSignatureScheme sig = mIdp.getSignatureScheme(); byte[] encrypted_key = new byte[32]; new Random().nextBytes(encrypted_key); long device_name = new Random().nextLong(); MessageDigest md; try { md = MessageDigest.getInstance("SHA-256"); } catch (NoSuchAlgorithmException e) { throw new RuntimeException("your platform does not support sha256", e); } md.update(encrypted_key); ByteBuffer bb = ByteBuffer.wrap(new byte[8]); bb.putLong(device_name); byte[] hash = md.digest(bb.array()); byte[] s = sig.sign(mMe, tdp.getSignatureKey(null, mMe), hash); for(int i = 0; i < ITERATIONS / 10; ++i) { try { md = MessageDigest.getInstance("SHA-256"); } catch (NoSuchAlgorithmException e) { throw new RuntimeException("your platform does not support sha256", e); } md.update(encrypted_key); bb = ByteBuffer.wrap(new byte[8]); bb.putLong(device_name); byte[] h = md.digest(bb.array()); sig.verify(mMe, s, h); } Date end = new Date(); Log.w(this.getName(), "Milliseconds per sign channel key " + (double)(end.getTime() - start.getTime()) / (ITERATIONS / 10)); } public void testShaHeaders() throws Corrupted { TransientTransportDataProvider tdp = new TransientTransportDataProvider(mIdp.getEncryptionScheme(), mIdp.getSignatureScheme(), mMe, null, null, null); MessageEncoder encoder = new MessageEncoder(tdp); Random rnd = new Random(); Secret s = new Secret(); s.h = new byte[32]; rnd.nextBytes(s.h); s.k = new byte[32]; rnd.nextBytes(s.k); s.q = rnd.nextLong(); MOutgoingSecret os = new MOutgoingSecret(); os.key_ = new byte[32]; rnd.nextBytes(os.key_); byte[] iv = encoder.randomSymetricCipherBlock(); Recipient r = new Recipient(); r.k = new byte[42]; rnd.nextBytes(r.k); r.s = new byte[21]; rnd.nextBytes(r.s); r.i = mMe.identity_; r.d = encoder.encodeSecret(s); r.d = encoder.encryptRecipientSecret(os, r.d, iv); Message m = new Message(); m.d = null; m.s = null; m.i = null; m.l = true; m.r = new Recipient[R + 1]; for(int i = 0; i <= R; ++i) { m.r[i] = r; } byte[] bytes = encoder.encodeMessage(m); Date start = new Date(); for(int i = 0; i < ITERATIONS; ++i) { Util.sha256(bytes); } Date end = new Date(); Log.w(this.getName(), "header size is " + bytes.length); Log.w(this.getName(), "Milliseconds per sha recipient " + (double)(end.getTime() - start.getTime()) / (ITERATIONS * m.r.length)); } public void testOverhead() throws Corrupted { TransientTransportDataProvider tdp = new TransientTransportDataProvider(mIdp.getEncryptionScheme(), mIdp.getSignatureScheme(), mMe, null, null, null); MessageEncoder encoder = new MessageEncoder(tdp); Random rnd = new Random(); Secret s = new Secret(); s.h = new byte[32]; rnd.nextBytes(s.h); s.k = new byte[32]; rnd.nextBytes(s.k); s.q = rnd.nextLong(); MOutgoingSecret os = new MOutgoingSecret(); os.key_ = new byte[32]; rnd.nextBytes(os.key_); byte[] iv = encoder.randomSymetricCipherBlock(); Recipient r = new Recipient(); r.k = new byte[42]; rnd.nextBytes(r.k); r.s = new byte[21]; rnd.nextBytes(r.s); r.i = mMe.identity_; r.d = encoder.encodeSecret(s); r.d = encoder.encryptRecipientSecret(os, r.d, iv); Message m = new Message(); m.d = null; m.s = null; m.i = null; m.l = true; m.r = new Recipient[1 + 1]; for(int i = 0; i < m.r.length; ++i) { m.r[i] = r; } byte[] bytes = encoder.encodeMessage(m); Log.w(this.getName(), "1-1 header size is " + bytes.length); m.r = new Recipient[229 + 1]; for(int i = 0; i < m.r.length; ++i) { m.r[i] = r; } bytes = encoder.encodeMessage(m); Log.w(this.getName(), "fb header size is " + bytes.length); m.r = new Recipient[20 + 1]; for(int i = 0; i < m.r.length; ++i) { m.r[i] = r; } bytes = encoder.encodeMessage(m); Log.w(this.getName(), "cont header size is " + bytes.length); m.r = new Recipient[10000 + 1]; for(int i = 0; i < m.r.length; ++i) { m.r[i] = r; } bytes = encoder.encodeMessage(m); Log.w(this.getName(), "twit header size is " + bytes.length); } public void testShaPerByte() { byte[] data = new byte[50 * 1024]; new Random().nextBytes(data); Date start = new Date(); for(int i = 0; i < ITERATIONS; ++i) { Util.sha256(data); } Date end = new Date(); Log.w(this.getName(), "Milliseconds per sha kbyte " + (double)(end.getTime() - start.getTime()) / (ITERATIONS * data.length / 1024)); } public void testEncryptAESSecret() throws Corrupted { TransientTransportDataProvider tdp = new TransientTransportDataProvider(mIdp.getEncryptionScheme(), mIdp.getSignatureScheme(), mMe, null, null, null); MessageEncoder encoder = new MessageEncoder(tdp); Random rnd = new Random(); Secret s = new Secret(); s.h = new byte[32]; rnd.nextBytes(s.h); s.k = new byte[32]; rnd.nextBytes(s.k); s.q = rnd.nextLong(); MOutgoingSecret os = new MOutgoingSecret(); os.key_ = new byte[32]; rnd.nextBytes(os.key_); byte[] iv = encoder.randomSymetricCipherBlock(); byte[] secret = encoder.encodeSecret(s); Date start = new Date(); for(int i = 0; i < ITERATIONS; ++i) { encoder.encryptRecipientSecret(os, secret, iv); } Date end = new Date(); Log.w(this.getName(), "Milliseconds per aes secret block " + (double)(end.getTime() - start.getTime()) / (ITERATIONS)); } public void testEncryptAESPerByte() { TransientTransportDataProvider tdp = new TransientTransportDataProvider(mIdp.getEncryptionScheme(), mIdp.getSignatureScheme(), mMe, null, null, null); MessageEncoder encoder = new MessageEncoder(tdp); byte[] data = new byte[50 * 1024]; new Random().nextBytes(data); byte[] key = new byte[32]; new Random().nextBytes(key); byte[] iv = encoder.randomSymetricCipherBlock(); Date start = new Date(); for(int i = 0; i < ITERATIONS; ++i) { encoder.encryptBody(key, data, iv); } Date end = new Date(); Log.w(this.getName(), "Milliseconds per aes kbyte " + (double)(end.getTime() - start.getTime()) / (ITERATIONS * data.length / 1024)); } public void testDecryptAESSecret() throws Corrupted { TransientTransportDataProvider tdp = new TransientTransportDataProvider(mIdp.getEncryptionScheme(), mIdp.getSignatureScheme(), mMe, null, null, null); MessageEncoder encoder = new MessageEncoder(tdp); MessageDecoder decoder = new MessageDecoder(tdp); Random rnd = new Random(); Secret s = new Secret(); s.h = new byte[32]; rnd.nextBytes(s.h); s.k = new byte[32]; rnd.nextBytes(s.k); s.q = rnd.nextLong(); MOutgoingSecret os = new MOutgoingSecret(); os.key_ = new byte[32]; rnd.nextBytes(os.key_); MIncomingSecret is = new MIncomingSecret(); is.key_ = os.key_; byte[] iv = encoder.randomSymetricCipherBlock(); byte[] secret = encoder.encodeSecret(s); secret = encoder.encryptRecipientSecret(os, secret, iv); Date start = new Date(); for(int i = 0; i < ITERATIONS; ++i) { decoder.decryptRecipientSecret(is, secret, iv); } Date end = new Date(); Log.w(this.getName(), "Milliseconds per decrypt aes secret block " + (double)(end.getTime() - start.getTime()) / (ITERATIONS)); } public void testDecryptAESPerByte() throws Corrupted { TransientTransportDataProvider tdp = new TransientTransportDataProvider(mIdp.getEncryptionScheme(), mIdp.getSignatureScheme(), mMe, null, null, null); MessageDecoder decoder = new MessageDecoder(tdp); MessageEncoder encoder = new MessageEncoder(tdp); byte[] data = new byte[50 * 1024]; new Random().nextBytes(data); byte[] key = new byte[32]; new Random().nextBytes(key); byte[] iv = new byte[16]; new Random().nextBytes(iv); data = encoder.encryptBody(key, data, iv); Date start = new Date(); for(int i = 0; i < ITERATIONS; ++i) { decoder.decryptBody(data, key, iv); } Date end = new Date(); Log.w(this.getName(), "Milliseconds per decrypt aes kbyte " + (double)(end.getTime() - start.getTime()) / (ITERATIONS * data.length / 1024)); } public void testDecryptChannelKey() { IBEncryptionScheme enc = mIdp.getEncryptionScheme(); ConversationKey k = enc.randomConversationKey(mMe); UserKey uk = enc.userKey(mMe); Date start = new Date(); for(int i = 0; i < ITERATIONS / 10; ++i) { enc.decryptConversationKey(uk, k.encryptedKey_); } Date end = new Date(); Log.w(this.getName(), "Milliseconds per decrypt channel key " + (double)(end.getTime() - start.getTime()) / (ITERATIONS / 10)); } }