package im.actor.core.modules.encryption.session; import java.util.HashSet; import java.util.Random; import im.actor.core.entity.encryption.PeerSession; import im.actor.core.util.RandomUtils; import im.actor.runtime.Crypto; import im.actor.runtime.Log; import im.actor.runtime.crypto.Curve25519; import im.actor.runtime.crypto.IntegrityException; import im.actor.runtime.crypto.box.ActorBox; import im.actor.runtime.crypto.box.ActorBoxKey; import im.actor.runtime.crypto.primitives.util.ByteStrings; import im.actor.runtime.crypto.ratchet.RatchetMessageKey; import im.actor.runtime.crypto.ratchet.RatchetPrivateKey; import im.actor.runtime.crypto.ratchet.RatchetPublicKey; import im.actor.runtime.crypto.ratchet.RatchetRootChainKey; public class EncryptedSessionChain { private PeerSession session; private byte[] ownPrivateKey; private byte[] theirPublicKey; private HashSet<Integer> receivedCounters; private int sentCounter; private byte[] rootChainKey; public EncryptedSessionChain(PeerSession session, byte[] ownPrivateKey, byte[] theirPublicKey) { this.session = session; this.ownPrivateKey = ownPrivateKey; this.theirPublicKey = theirPublicKey; this.receivedCounters = new HashSet<>(); this.sentCounter = 0; this.rootChainKey = RatchetRootChainKey.makeRootChainKey( new RatchetPrivateKey(ownPrivateKey), new RatchetPublicKey(theirPublicKey), session.getMasterKey()); } public PeerSession getSession() { return session; } public byte[] getOwnPrivateKey() { return ownPrivateKey; } public byte[] getTheirPublicKey() { return theirPublicKey; } public byte[] decrypt(byte[] data) throws IntegrityException { if (data.length < 88) { throw new IntegrityException("Data length is too small"); } // // Parsing message header // final int senderKeyGroupId = ByteStrings.bytesToInt(data, 0); final long senderEphermalKey0Id = ByteStrings.bytesToLong(data, 4); final long receiverEphermalKey0Id = ByteStrings.bytesToLong(data, 12); final byte[] senderEphemeralKey = ByteStrings.substring(data, 20, 32); final byte[] receiverEphemeralKey = ByteStrings.substring(data, 52, 32); final int messageIndex = ByteStrings.bytesToInt(data, 84); // // Validating header // // if (senderKeyGroupId != session.getPeerKeyGroupId()) { // throw new IntegrityException("Incorrect sender key group id"); // } // if (senderEphermalKey0Id != session.getTheirPreKey().getKeyId()) { // throw new IntegrityException("Incorrect sender pre key id"); // } // if (receiverEphermalKey0Id != session.getOwnPreKey().getKeyId()) { // throw new IntegrityException("Incorrect receiver pre key id"); // } // if (ByteStrings.isEquals(senderEphemeralKey, theirPublicKey)) { // throw new IntegrityException("Incorrect sender ephemeral key"); // } // if (ByteStrings.isEquals(receiverEphemeralKey, ownPrivateKey)) { // throw new IntegrityException("Incorrect receiver ephemeral key"); // } // // Decryption // ActorBoxKey ratchetMessageKey = RatchetMessageKey.buildKey(rootChainKey, messageIndex); byte[] header = ByteStrings.substring(data, 0, 88); byte[] message = ByteStrings.substring(data, 88, data.length - 88); return ActorBox.openBox(header, message, ratchetMessageKey); } public byte[] encrypt(byte[] data) throws IntegrityException { int messageIndex = sentCounter++; ActorBoxKey ratchetMessageKey = RatchetMessageKey.buildKey(rootChainKey, messageIndex); byte[] header = ByteStrings.merge( ByteStrings.intToBytes(session.getOwnKeyGroupId()), ByteStrings.longToBytes(session.getOwnPreKeyId()), /*Alice Initial Ephermal*/ ByteStrings.longToBytes(session.getTheirPreKeyId()), /*Bob Initial Ephermal*/ Curve25519.keyGenPublic(ownPrivateKey), theirPublicKey, ByteStrings.intToBytes(messageIndex)); /* Message Index */ // Log.d("EncryptedSessionChain#" + session.getPeerKeyGroupId(), "Own ephemeral Key: " + Crypto.keyHash(Curve25519.keyGenPublic(ownPrivateKey))); // Log.d("EncryptedSessionChain#" + session.getPeerKeyGroupId(), "Their ephemeral Key: " + Crypto.keyHash(theirPublicKey)); return ByteStrings.merge(header, ActorBox.closeBox(header, data, Crypto.randomBytes(32), ratchetMessageKey)); } public void safeErase() { for (int i = 0; i < ownPrivateKey.length; i++) { ownPrivateKey[i] = (byte) RandomUtils.randomId(255); } for (int i = 0; i < theirPublicKey.length; i++) { theirPublicKey[i] = (byte) RandomUtils.randomId(255); } for (int i = 0; i < rootChainKey.length; i++) { rootChainKey[i] = (byte) RandomUtils.randomId(255); } receivedCounters.clear(); sentCounter = 0; } }