package network.thunder.core.communication.objects.messages.impl; import network.thunder.core.communication.objects.messages.impl.message.lnpayment.OnionObject; import network.thunder.core.communication.objects.messages.impl.message.lnpayment.PeeledOnion; import network.thunder.core.communication.objects.messages.interfaces.helper.LNOnionHelper; import network.thunder.core.etc.Tools; import network.thunder.core.etc.crypto.CryptoTools; import network.thunder.core.etc.crypto.ECDH; import network.thunder.core.etc.crypto.ECDHKeySet; import org.bitcoinj.core.ECKey; import java.util.List; /** * Created by matsjerratsch on 08/12/2015. */ public class LNOnionHelperImpl implements LNOnionHelper { @Override public PeeledOnion loadMessage (ECKey key, OnionObject encryptedOnionObject) { ECDHKeySet keySet = getKeySet(key, encryptedOnionObject); byte[] unencrypted = decryptMessage(keySet, encryptedOnionObject); byte[] payload = new byte[OnionObject.DATA_LENGTH]; System.arraycopy(unencrypted, 0, payload, 0, OnionObject.DATA_LENGTH); OnionObject nextObject = getMessageForNextHop(keySet, unencrypted); return new PeeledOnion(nextObject, payload); } private static OnionObject getMessageForNextHop (ECDHKeySet keySet, byte[] decryptedData) { byte[] padding = new byte[OnionObject.TOTAL_LENGTH]; byte[] paddingEnc = CryptoTools.encryptAES_CTR(padding, keySet.encryptionKey, keySet.ivClient, 0); byte[] data = new byte[OnionObject.TOTAL_LENGTH * OnionObject.MAX_HOPS]; System.arraycopy(decryptedData, OnionObject.DATA_LENGTH, data, 0, decryptedData.length - OnionObject.DATA_LENGTH); System.arraycopy(paddingEnc, 0, data, getEncryptedLengthNextHop(), paddingEnc.length); return new OnionObject(data); } public static int getEncryptedLengthNextHop () { return (OnionObject.TOTAL_LENGTH * (OnionObject.MAX_HOPS - 1)); } private static byte[] decryptMessage (ECDHKeySet keySet, OnionObject encryptedOnionObject) { byte[] hmac = new byte[OnionObject.HMAC_LENGTH]; byte[] data = new byte[OnionObject.TOTAL_LENGTH * OnionObject.MAX_HOPS - (OnionObject.KEY_LENGTH + OnionObject.HMAC_LENGTH)]; System.arraycopy(encryptedOnionObject.data, OnionObject.KEY_LENGTH, hmac, 0, hmac.length); System.arraycopy(encryptedOnionObject.data, OnionObject.KEY_LENGTH + hmac.length, data, 0, data.length); byte[] decryptedData = CryptoTools.decryptAES_CTR(data, keySet.encryptionKey, keySet.ivServer, 0); byte[] dataToSign = new byte[OnionObject.DATA_LENGTH]; System.arraycopy(decryptedData, 0, dataToSign, 0, OnionObject.DATA_LENGTH); CryptoTools.checkHMAC(hmac, dataToSign, keySet.hmacKey); return decryptedData; } private static ECDHKeySet getKeySet (ECKey keyServer, OnionObject encryptedOnionObject) { byte[] key = new byte[OnionObject.KEY_LENGTH]; System.arraycopy(encryptedOnionObject.data, 0, key, 0, key.length); ECKey ephemeralKey = ECKey.fromPublicOnly(key); ECDHKeySet keySet = ECDH.getSharedSecret(keyServer, ephemeralKey); return keySet; } @Override public OnionObject createOnionObject (List<byte[]> nodeList, byte[] payload) { System.out.println("createOnionObject"); for (byte[] b : nodeList) { System.out.println(Tools.bytesToHex(b)); } if (nodeList.size() > OnionObject.MAX_HOPS) { throw new RuntimeException("Too many nodes in nodeList"); } int byteCount = OnionObject.MAX_HOPS * OnionObject.TOTAL_LENGTH; byte[] data = Tools.getRandomByte(byteCount); for (int i = 0; i < nodeList.size(); ++i) { byte[] temp = new byte[byteCount]; byte[] dataToSign = new byte[OnionObject.DATA_LENGTH]; System.arraycopy(data, 0, temp, OnionObject.DATA_LENGTH, data.length - OnionObject.DATA_LENGTH); ECKey key = ECKey.fromPublicOnly(nodeList.get(nodeList.size() - 1 - i)); ECKey keyServer = CryptoTools.getEphemeralKey(); ECDHKeySet keySet = ECDH.getSharedSecret(keyServer, key); if (i > 0) { byte[] nextNode = nodeList.get(nodeList.size() - i); System.arraycopy(nextNode, 0, dataToSign, 0, nextNode.length); } System.arraycopy(dataToSign, 0, temp, 0, dataToSign.length); byte[] encryptedTemp = CryptoTools.encryptAES_CTR(temp, keySet.encryptionKey, keySet.ivClient, 0); byte[] hmac = CryptoTools.getHMAC(dataToSign, keySet.hmacKey); data = new byte[OnionObject.MAX_HOPS * OnionObject.TOTAL_LENGTH]; System.arraycopy(keyServer.getPubKey(), 0, data, 0, OnionObject.KEY_LENGTH); System.arraycopy(hmac, 0, data, OnionObject.KEY_LENGTH, hmac.length); System.arraycopy(encryptedTemp, 0, data, OnionObject.KEY_LENGTH + OnionObject.HMAC_LENGTH, encryptedTemp.length - OnionObject.KEY_LENGTH - OnionObject.HMAC_LENGTH); } return new OnionObject(data); } }