/*
* 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 de.uni_bonn.bit.wallet_protocol.*;
import org.spongycastle.math.ec.ECPoint;
import org.spongycastle.pqc.math.linearalgebra.IntegerFunctions;
import java.math.BigInteger;
import java.util.concurrent.ForkJoinTask;
/**
* This class implements the desktop's part of the two-party ECDSA signature protocol. The desktop takes the side of Alice
* in the protocol. In the end, this class returns an ECDSA signature.
*/
public class DesktopSigner {
static final BigInteger nEC = ECKey.CURVE.getN();
PaillierKeyPair pkpDesktop;
PaillierKeyPair pkpPhone;
BCParameters desktopBCParameters;
BCParameters phoneBCParameters;
BigInteger privateKey;
ECPoint otherPublicKey;
MultiThreadingHelper zkProofHelper;
protected BigInteger kDesktop;
protected BigInteger zDesktop;
protected BigInteger r1;
protected BigInteger alphaDesktop;
protected BigInteger r2;
protected BigInteger beta;
protected ECPoint RPhone;
protected ECPoint R;
protected States state;
public DesktopSigner(ECKey privateKey, ECKey otherPublicKey, PaillierKeyPair pkpDesktop, PaillierKeyPair pkpPhone,
BCParameters desktopBCParameters, BCParameters phoneBCParameters) {
this.privateKey = BitcoinECMathHelper.convertPrivKeyToBigInt(privateKey);
this.otherPublicKey = BitcoinECMathHelper.convertPubKeyToPoint(otherPublicKey);
this.desktopBCParameters = desktopBCParameters;
this.phoneBCParameters = phoneBCParameters;
this.pkpDesktop = pkpDesktop;
this.pkpPhone = pkpPhone;
this.zkProofHelper = new MultiThreadingHelper();
this.state = States.ComputeSignatureParts;
}
public SignatureParts computeSignatureParts(){
if(! state.equals(States.ComputeSignatureParts))
throw new ProtocolException("Operation not allowed in this protocol state.");
kDesktop = IntegerFunctions.randomize(nEC);
zDesktop = kDesktop.modInverse(nEC);
r1 = pkpDesktop.generateRandomizer();
r2 = pkpDesktop.generateRandomizer();
BigInteger nsquaredDesktop = pkpDesktop.getN().pow(2);
ForkJoinTask<BigInteger> betaFuture = zkProofHelper.PowMult(pkpDesktop.getG(), privateKey.multiply(zDesktop).mod(nEC), r2, pkpDesktop.getN(), nsquaredDesktop);
ForkJoinTask<BigInteger> alphaDesktopFuture = zkProofHelper.PowMult(pkpDesktop.getG(), zDesktop, r1, pkpDesktop.getN(), nsquaredDesktop);
beta = betaFuture.join();
alphaDesktop = alphaDesktopFuture.join();
state = States.ComputeEphemeralPublicValue;
return new SignatureParts(alphaDesktop, beta);
}
public EphemeralPublicValueWithProof computeEphemeralPublicValue(EphemeralValueShare ephemeralValueShare){
if(! state.equals(States.ComputeEphemeralPublicValue))
throw new ProtocolException("Operation not allowed in this protocol state.");
RPhone = ephemeralValueShare.getRPhone();
// We first check that RPhone is associated with the correct curve and then we check that it is on the associated curve.
if(! (RPhone.getCurve().equals(ECKey.CURVE.getCurve()) && RPhone.isValid())){
throw new ProtocolException("The point RPhone provided by the phone is invalid");
}
R = RPhone.multiply(kDesktop).normalize();
ECPoint QDesktop = ECKey.CURVE.getG().multiply(privateKey).normalize();
ZKProofDesktop proof = ZKProofDesktop.generateProof(zDesktop, privateKey.multiply(zDesktop).mod(nEC), r1, r2, R, RPhone, QDesktop,
ECKey.CURVE.getG(), alphaDesktop, beta, pkpDesktop, desktopBCParameters, zkProofHelper);
state = States.DecryptEncryptedSignature;
return new EphemeralPublicValueWithProof(R, proof);
}
public ECKey.ECDSASignature decryptEncryptedSignature(EncryptedSignatureWithProof encryptedSignature, byte[] hash){
if(! state.equals(States.DecryptEncryptedSignature))
throw new ProtocolException("Operation not allowed in this protocol state.");
if(encryptedSignature.getSigma().compareTo(BigInteger.ONE) < 0
|| encryptedSignature.getSigma().compareTo(pkpDesktop.getN().pow(2)) >= 0){
throw new ProtocolException("Sigma is out of bounds.");
}
if(encryptedSignature.getAlphaPhone().compareTo(BigInteger.ONE) < 0
|| encryptedSignature.getAlphaPhone().compareTo(pkpPhone.getN().pow(2)) >=0 ){
throw new ProtocolException("alpha_B is out of bounds.");
}
BigInteger hm = new BigInteger(1, hash);
BigInteger r = R.normalize().getAffineXCoord().toBigInteger().mod(nEC);
BigInteger nsquared = pkpDesktop.getN().pow(2);
ForkJoinTask<BigInteger> c1 = zkProofHelper.PowMult(alphaDesktop, hm, nsquared);
ForkJoinTask<BigInteger> c2 = zkProofHelper.PowMult(beta, r, nsquared);
encryptedSignature.getProof().verify(c1.join(), c2.join(),
encryptedSignature.getSigma(), encryptedSignature.getAlphaPhone(), ECKey.CURVE.getG(), otherPublicKey, RPhone,
pkpDesktop, pkpPhone, phoneBCParameters, zkProofHelper);
long start = System.currentTimeMillis();
BigInteger s = pkpDesktop.decrypt(encryptedSignature.getSigma()).mod(nEC);
System.out.println("decrypt time " + (System.currentTimeMillis() - start) + "ms");
state = States.Finished;
return new ECKey.ECDSASignature(r, s);
}
protected enum States{
ComputeSignatureParts, ComputeEphemeralPublicValue, DecryptEncryptedSignature, Finished
}
}