package aalg.crypto;

import java.io.IOException;
import java.io.InputStream;
import java.math.BigInteger;
import aalg.algebra.IElement;
import aalg.algebra.IGroup;
import aalg.algebra.SubGroupDefinition;
import aalg.algebra.Utils;

/**
 * Class DsaVerifier verifies messages signed by DsaSigner.
 * @author Ing. Petr Ležák
 *
 * @param <T> type of group element
 */
public class DsaVerifier<T extends IElement> implements ISignatureVerifier<DsaSignature, DlPublicKey<T>> {
	
	private final SubGroupDefinition<T> subGroupDefinition;
	private final IHashFunction hashFunction;
	private final IAsymmetricPadding padding; 
	
	public DsaVerifier (final SubGroupDefinition<T> subGroupDefinition, final IHashFunction hashFunction, final IAsymmetricPadding padding) {
		this.subGroupDefinition = subGroupDefinition;
		this.hashFunction = hashFunction;
		this.padding = padding;
		
		if (!subGroupDefinition.getOrder().isProbablePrime(100))
			throw new RuntimeException("Subgroup has compound order");
	}
	
	/**
	 * Verifies signature of given message.
	 * @param message message to verify
	 * @param signature signature signature of the message
	 * @param publicKey public key of the signing entity
	 * @return true if signature is valid, false if not
	 */
	public boolean verify (final byte[] message, final DsaSignature signature, final DlPublicKey<T> publicKey) {
		final IGroup<T> group = subGroupDefinition.getGroup();
		final BigInteger q = subGroupDefinition.getOrder();
		
		final BigInteger h = padding.bytesToNumber(hashFunction.calculateHash (message));
		final T g = subGroupDefinition.getGenerator();
		
		final BigInteger r = signature.getR();
		final BigInteger s = signature.getS();
		
		if (r.equals(BigInteger.ZERO) || s.equals(BigInteger.ZERO))
			return false;
		
		final BigInteger invS = s.modInverse(q);
		
		final T t1 = group.repeatOperation(g, invS.multiply (h));
		final T t2 = group.repeatOperation(publicKey.getKey(), invS.multiply (r));
		final T t = group.operation(t1, t2);
		System.out.println ("t = " + t);
		
		final BigInteger calculatedR = group.getElementIndex(t).mod(q);
		
		return calculatedR.equals(r);
	}
	
	/**
	 * Reads signature from given stream.
	 * @param stream stream with signature
	 * @return signature
	 */
	public DsaSignature readSignatureFromStream(final InputStream stream) throws IOException {
		final BigInteger r = Utils.readNumber(stream);
		final BigInteger s = Utils.readNumber(stream);
		
		return new DsaSignature(r, s);
	}

}
