package com.ripple.crypto.ecdsa; import com.ripple.utils.HashUtils; import com.ripple.utils.Utils; import org.ripple.bouncycastle.crypto.params.ECPrivateKeyParameters; import org.ripple.bouncycastle.crypto.params.ECPublicKeyParameters; import org.ripple.bouncycastle.crypto.signers.ECDSASigner; import org.ripple.bouncycastle.math.ec.ECPoint; import java.math.BigInteger; public class KeyPair implements IKeyPair { BigInteger priv, pub; byte[] pubBytes; @Override public BigInteger pub() { return pub; } @Override public byte[] pubBytes() { return pubBytes; } public KeyPair(BigInteger priv, BigInteger pub) { this.priv = priv; this.pub = pub; this.pubBytes = pub.toByteArray(); } @Override public BigInteger priv() { return priv; } @Override public boolean verify(byte[] data, byte[] sigBytes) { return verify(data, sigBytes, pub); } @Override public byte[] sign(byte[] bytes) { return sign(bytes, priv); } @Override public byte[] public_key_160_hash() { return HashUtils.SHA256_RIPEMD160(pubBytes); } @Override public String pubHex() { return Utils.bigHex(pub); } @Override public String privHex() { return Utils.bigHex(priv); } public static boolean verify(byte[] data, byte[] sigBytes, BigInteger pub) { ECDSASignature signature = ECDSASignature.decodeFromDER(sigBytes); ECDSASigner signer = new ECDSASigner(); ECPoint pubPoint = SECP256K1.curve().decodePoint(pub.toByteArray()); ECPublicKeyParameters params = new ECPublicKeyParameters(pubPoint, SECP256K1.params()); signer.init(false, params); try { return signer.verifySignature(data, signature.r, signature.s); } catch (NullPointerException e) { e.printStackTrace(); return false; } } public static byte[] sign(byte[] bytes, BigInteger secret) { ECDSASignature sig = createECDSASignature(bytes, secret); byte[] der = sig.encodeToDER(); if (!isStrictlyCanonical(der)) { throw new IllegalStateException("Signature is not strictly canonical"); } return der; } public static boolean isStrictlyCanonical(byte[] sig) { return checkIsCanonical(sig, true); } public static boolean checkIsCanonical(byte[] sig, boolean strict) { // Make sure signature is canonical // To protect against signature morphing attacks // Signature should be: // <30> <len> [ <02> <lenR> <R> ] [ <02> <lenS> <S> ] // where // 6 <= len <= 70 // 1 <= lenR <= 33 // 1 <= lenS <= 33 int sigLen = sig.length; if ((sigLen < 8) || (sigLen > 72)) return false; if ((sig[0] != 0x30) || (sig[1] != (sigLen - 2))) return false; // Find R and check its length int rPos = 4, rLen = sig[rPos - 1]; if ((rLen < 1) || (rLen > 33) || ((rLen + 7) > sigLen)) return false; // Find S and check its length int sPos = rLen + 6, sLen = sig[sPos - 1]; if ((sLen < 1) || (sLen > 33) || ((rLen + sLen + 6) != sigLen)) return false; if ((sig[rPos - 2] != 0x02) || (sig[sPos - 2] != 0x02)) return false; // R or S have wrong type if ((sig[rPos] & 0x80) != 0) return false; // R is negative if ((sig[rPos] == 0) && rLen == 1) return false; // R is zero if ((sig[rPos] == 0) && ((sig[rPos + 1] & 0x80) == 0)) return false; // R is padded if ((sig[sPos] & 0x80) != 0) return false; // S is negative if ((sig[sPos] == 0) && sLen == 1) return false; // S is zero if ((sig[sPos] == 0) && ((sig[sPos + 1] & 0x80) == 0)) return false; // S is padded byte[] rBytes = new byte[rLen]; byte[] sBytes = new byte[sLen]; System.arraycopy(sig, rPos, rBytes, 0, rLen); System.arraycopy(sig, sPos, sBytes, 0, sLen); BigInteger r = new BigInteger(1, rBytes), s = new BigInteger(1, sBytes); BigInteger order = SECP256K1.order(); if (r.compareTo(order) != -1 || s.compareTo(order) != -1) { return false; // R or S greater than modulus } if (strict) { return order.subtract(s).compareTo(s) != -1; } else { return true; } } private static ECDSASignature createECDSASignature(byte[] bytes, BigInteger secret) { ECDSASigner signer = new ECDSASigner(); ECPrivateKeyParameters privKey = new ECPrivateKeyParameters(secret, SECP256K1.params()); signer.init(true, privKey); BigInteger[] sigs = signer.generateSignature(bytes); BigInteger r = sigs[0], s = sigs[1]; BigInteger otherS = SECP256K1.order().subtract(s); if (s.compareTo(otherS) == 1) { s = otherS; } return new ECDSASignature(r, s); } }