/*
* ThunderNetwork - Server Client Architecture to send Off-Chain Bitcoin Payments
* Copyright (C) 2015 Mats Jerratsch <matsjj@gmail.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
package network.thunder.core.lightning;
import network.thunder.core.etc.Tools;
import java.nio.ByteBuffer;
import java.util.Arrays;
/*
* For deterministic derivation of revocation hashes, use the following scheme
* Chose a master seed - for example the hash of private key.
* For obtaining a new childseed, hash the parentseed twice using SHA256.
* For obtaining a sibling of a seed, concat the hash with the 4-byte integer of the sibling id.
* The preimage will be the sibling seed hashed with RIPEMD160(SHA256) and the public hash will be the preimage hashed with RIPEMD160(SHA256) again.
*
* This ensures that one can give away the preimage without revealing all other siblings.
* Revealing the childseed will reveal all further children and all siblings.
*/
public class HashDerivation {
/**
* The other party has breached the contract and submitted an old channel transaction.
*
* @param seed The latest masterseed we received from the other party
* @param target The hash we are looking for
* @param maxChildTries The maximum depth we will search to before giving up..
* @param maxSiblingTries The amount of siblings calculating for each depth
* @return The preimage hashing to the desired hash.
*/
public static RevocationHash bruteForceHash (byte[] seed, byte[] target, int maxChildTries, int maxSiblingTries) {
for (int i = 0; i < maxChildTries; i++) {
for (int j = 0; j < maxSiblingTries; j++) {
RevocationHash test = HashDerivation.calculateRevocationHash(seed, 0, j);
if (Arrays.equals(test.getSecretHash(), target)) {
return new RevocationHash(i, j, test.getSecret(), test.getSecretHash());
}
}
seed = Tools.hashSecret(seed);
}
return null;
}
/**
* Calculate a revocation hash for a new channel state.
*
* @param seed A masterseed for this calculation
* @param depth The depth of the new RevocationHash
* @param childNumber The n. sibling at the specified depth
*/
public static RevocationHash calculateRevocationHash (byte[] seed, int depth, int childNumber) {
byte[] childseed = seed;
for (int i = 0; i < depth; i++) {
childseed = Tools.hashSecret(childseed);
}
if (childNumber == 0) {
return new RevocationHash(depth, childNumber, childseed, null);
}
byte[] childseedWithNumber = new byte[24];
System.arraycopy(childseed, 0, childseedWithNumber, 0, 20);
ByteBuffer buffer = ByteBuffer.allocate(4);
buffer.putInt(childNumber);
buffer.flip();
System.arraycopy(buffer.array(), 0, childseedWithNumber, 20, 4);
byte[] secret = Tools.hashSecret(childseedWithNumber);
byte[] secretHash = Tools.hashSecret(secret);
return new RevocationHash(depth, childNumber, secret, secretHash);
}
}