/*
* Copyright 2014 Christopher Mann
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package de.uni_bonn.bit;
import org.bitcoinj.core.ECKey;
import org.spongycastle.crypto.digests.SHA512Digest;
import org.spongycastle.math.ec.ECPoint;
import java.math.BigInteger;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ForkJoinPool;
import java.util.concurrent.ForkJoinTask;
import java.util.concurrent.RecursiveTask;
/**
* This class contains several methods to perform mathematical operations. Most importantly, it contains methods
* which perform expensive ECPoint and BigInteger operations with multi threading. Java's ForkJoinTask is used for the
* multi threading and this class keeps an instance of ForkJoinPool to execute the tasks.
*/
public class MultiThreadingHelper {
private static final BigInteger nEC = ECKey.CURVE.getN();
public static BigInteger hash(String id, Object... data){
return hash512(id, data).mod(nEC);
}
public static BigInteger hash512(String id, Object... data){
System.out.println("hash() called with parameters:");
System.out.println(id);
SHA512Digest digest = new SHA512Digest();
digest.update(id.getBytes(), 0, id.getBytes().length);
for(Object obj : data){
if( obj instanceof ECPoint){
byte[] pointData = ((ECPoint) obj).normalize().getEncoded(true);
System.out.println(obj.toString());
digest.update(pointData, 0, pointData.length);
}else if (obj instanceof BigInteger){
byte[] bigIntData = ((BigInteger) obj).toByteArray();
System.out.println(obj.toString());
digest.update(bigIntData, 0, bigIntData.length);
}else{
throw new RuntimeException("Wrong type in hash function: " + obj.getClass().toString());
}
}
byte[] resultBytes = new byte[64];
digest.doFinal(resultBytes, 0);
return new BigInteger(1, resultBytes);
}
public static void dumpBitLengthOfValues(BigInteger... data) {
System.out.println("Bitlengths:");
for (BigInteger bigInt : data) {
System.out.println(bigInt.bitLength());
}
}
public ECPoint[] multiplyPointsWithScalars(BigInteger[] scalars, ECPoint[] points){
ECPoint[] results = new ECPoint[points.length];
for(int i = 0; i < points.length; i++){
results[i] = points[i].multiply(scalars[i]);
}
return results;
}
private ForkJoinPool pool;
private ForkJoinPool getPool(){
if(pool == null){
pool = new ForkJoinPool();
}
return pool;
}
/**
* Sums up scalar multiplied points.
* @param values a, P, b, Q, c, R
* @return a*P+b*Q+c*R
*/
public ForkJoinTask<ECPoint> pointMultAdd(Object... values){
return getPool().submit(new PointMultAddTask(values));
}
/**
* Multiplies several modpowed numbers.
* @param values a,b,c,d,e,f,q
* @return a^b*c^d*e^f mod q
*/
public ForkJoinTask<BigInteger> PowMult(Object... values){
return getPool().submit(new PowMultTask(values));
}
public static class PointMultAddTask extends RecursiveTask<ECPoint> {
final Object[] values;
boolean normalize = true;
public PointMultAddTask(Object... values) {
this.values = values;
}
@Override
protected ECPoint compute() {
if(values.length == 2){
final BigInteger scalar = (BigInteger) values[0];
final ECPoint point = (ECPoint) values[1];
ECPoint result = point.multiply(scalar.mod(nEC));
if(normalize){
result = result.normalize();
}
return result;
}else{
List<ForkJoinTask<ECPoint>> futures = new ArrayList<>();
for(int i = 2; i < values.length; i += 2){
PointMultAddTask pma = new PointMultAddTask(values[i], values[i+1]);
pma.normalize = false;
futures.add(pma.fork());
}
PointMultAddTask task = new PointMultAddTask(values[0], values[1]);
task.normalize = false;
ECPoint result = task.compute();
for(ForkJoinTask<ECPoint> future : futures){
result = result.add(future.join());
}
if(normalize){
result = result.normalize();
}
return result;
}
}
}
public static class PowMultTask extends RecursiveTask<BigInteger> {
final Object[] values;
public PowMultTask(Object... values) {
this.values = values;
}
@Override
protected BigInteger compute() {
if(values.length == 3){
final BigInteger a = (BigInteger) values[0];
final BigInteger b = (BigInteger) values[1];
final BigInteger q = (BigInteger) values[2];
BigInteger result = a.modPow(b,q);
return result;
}else{
List<ForkJoinTask<BigInteger>> futures = new ArrayList<>();
BigInteger q = (BigInteger) values[values.length - 1];
for(int i = 2; i < values.length - 1; i += 2){
PowMultTask pma = new PowMultTask(values[i], values[i+1], q);
futures.add(pma.fork());
}
PowMultTask task = new PowMultTask(values[0], values[1], q);
BigInteger result = task.compute();
for(ForkJoinTask<BigInteger> future : futures){
result = result.multiply(future.join()).mod(q);
}
return result;
}
}
}
}