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;
}
}