All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.bccapi.bitlib.crypto.PublicKey Maven / Gradle / Ivy

There is a newer version: 1.0.3
Show newest version
package com.bccapi.bitlib.crypto;

import com.bccapi.bitlib.crypto.ec.EcTools;
import com.bccapi.bitlib.crypto.ec.Parameters;
import com.bccapi.bitlib.crypto.ec.Point;
import com.bccapi.bitlib.util.ByteReader;
import com.bccapi.bitlib.util.ByteReader.InsufficientBytesException;
import com.bccapi.bitlib.util.HashUtils;
import com.bccapi.bitlib.util.HexUtils;

import java.io.Serializable;
import java.math.BigInteger;
import java.util.Arrays;

public class PublicKey implements Serializable {

   private static final long serialVersionUID = 1L;

   private final byte[] _pubKeyBytes;
   private byte[] _pubKeyHash;
   private Point _Q;

   public PublicKey(byte[] publicKeyBytes) {
      _pubKeyBytes = publicKeyBytes;
   }

   public byte[] getPublicKeyBytes() {
      return _pubKeyBytes;
   }

   public byte[] getPublicKeyHash() {
      if (_pubKeyHash == null) {
         _pubKeyHash = HashUtils.addressHash(_pubKeyBytes);
      }
      return _pubKeyHash;
   }

   @Override
   public int hashCode() {
      byte[] hash = getPublicKeyHash();
      return ((int) hash[0]) + (((int) hash[1]) << 8) + (((int) hash[1]) << 16) + (((int) hash[1]) << 32);
   }

   @Override
   public boolean equals(Object obj) {
      if (!(obj instanceof PublicKey)) {
         return false;
      }
      PublicKey other = (PublicKey) obj;
      return Arrays.equals(getPublicKeyHash(), other.getPublicKeyHash());
   }

   @Override
   public String toString() {
      return HexUtils.toHex(_pubKeyBytes);
   }

   public boolean verifyStandardBitcoinSignature(byte[] data, byte[] signature) {
      // Decode parameters r and s
      ByteReader reader = new ByteReader(signature);

      BigInteger[] params = decodeSignatureParameters(reader);
      if (params == null) {
         return false;
      }
      // Make sure that we have a hash type at the end
      if (reader.available() != 1) {
         return false;
      }
      return verifySignature(data, params[0], params[1], getQ());
   }

   /**
    * Is this a compressed public key?
    *
    * @return compressed
    */
   public boolean isCompressed() {
      return getQ().isCompressed();
   }

   private Point getQ() {
      if (_Q == null) {
         _Q = Parameters.curve.decodePoint(_pubKeyBytes);
      }
      return _Q;
   }

   private static BigInteger[] decodeSignatureParameters(ByteReader reader) {
      try {
         // Read tag, must be 0x30
         if ((((int) reader.get()) & 0xFF) != 0x30) {
            return null;
         }

         // Read total length as a byte, standard inputs never get longer than
         // this
         int length = ((int) reader.get()) & 0xFF;

         // Read first type, must be 0x02
         if ((((int) reader.get()) & 0xFF) != 0x02) {
            return null;
         }

         // Read first length
         int length1 = ((int) reader.get()) & 0xFF;

         // Read first byte array
         byte[] bytes1 = reader.getBytes(length1);

         // Make sure BigInteger regards it as positive
         bytes1 = makePositive(bytes1);

         // Read second type, must be 0x02
         if ((((int) reader.get()) & 0xFF) != 0x02) {
            return null;
         }

         // Read second length
         int length2 = ((int) reader.get()) & 0xFF;

         // Read second byte array
         byte[] bytes2 = reader.getBytes(length2);

         // Make sure BigInteger regards it as positive
         bytes2 = makePositive(bytes2);

         // Validate that the lengths add up to the total
         if (2 + length1 + 2 + length2 != length) {
            return null;
         }

         BigInteger[] result = new BigInteger[] { new BigInteger(bytes1), new BigInteger(bytes2) };
         return result;
      } catch (InsufficientBytesException e) {
         return null;
      }
   }

   private static byte[] makePositive(byte[] bytes) {
      if (bytes[0] < 0) {
         byte[] temp = new byte[bytes.length + 1];
         System.arraycopy(bytes, 0, temp, 1, bytes.length);
         return temp;
      }
      return bytes;
   }

   private static boolean verifySignature(byte[] message, BigInteger r, BigInteger s, Point Q) {
      BigInteger n = Parameters.n;
      BigInteger e = calculateE(n, message);

      // r in the range [1,n-1]
      if (r.compareTo(BigInteger.ONE) < 0 || r.compareTo(n) >= 0) {
         return false;
      }

      // s in the range [1,n-1]
      if (s.compareTo(BigInteger.ONE) < 0 || s.compareTo(n) >= 0) {
         return false;
      }

      BigInteger c = s.modInverse(n);

      BigInteger u1 = e.multiply(c).mod(n);
      BigInteger u2 = r.multiply(c).mod(n);

      Point G = Parameters.G;

      Point point = EcTools.sumOfTwoMultiplies(G, u1, Q, u2);

      BigInteger v = point.getX().toBigInteger().mod(n);

      return v.equals(r);
   }

   private static BigInteger calculateE(BigInteger n, byte[] message) {
      if (n.bitLength() > message.length * 8) {
         return new BigInteger(1, message);
      } else {
         int messageBitLength = message.length * 8;
         BigInteger trunc = new BigInteger(1, message);

         if (messageBitLength - n.bitLength() > 0) {
            trunc = trunc.shiftRight(messageBitLength - n.bitLength());
         }

         return trunc;
      }
   }

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy