diff --git a/common/src/main/java/org/tron/common/parameter/CommonParameter.java b/common/src/main/java/org/tron/common/parameter/CommonParameter.java index a73158a718a..f853dfa648f 100644 --- a/common/src/main/java/org/tron/common/parameter/CommonParameter.java +++ b/common/src/main/java/org/tron/common/parameter/CommonParameter.java @@ -335,7 +335,7 @@ public class CommonParameter { @Getter @Setter - public boolean allowShieldedTransactionApi; // clearParam: true + public boolean allowShieldedTransactionApi; // clearParam: false @Getter @Setter public long blockNumForEnergyLimit; diff --git a/crypto/src/main/java/org/tron/common/crypto/ECKey.java b/crypto/src/main/java/org/tron/common/crypto/ECKey.java index d0a6048aca1..5117df337dc 100644 --- a/crypto/src/main/java/org/tron/common/crypto/ECKey.java +++ b/crypto/src/main/java/org/tron/common/crypto/ECKey.java @@ -31,7 +31,6 @@ import java.security.interfaces.ECPublicKey; import java.security.spec.InvalidKeySpecException; import java.util.Arrays; -import java.util.Objects; import javax.annotation.Nullable; import lombok.extern.slf4j.Slf4j; import org.bouncycastle.asn1.sec.SECNamedCurves; @@ -68,17 +67,21 @@ public class ECKey implements Serializable, SignInterface { public static final ECParameterSpec CURVE_SPEC; /** - * Equal to CURVE.getN().shiftRight(1), used for canonicalising the S value of a signature. ECDSA - * signatures are mutable in the sense that for a given (R, S) pair, then both (R, S) and (R, N - - * S mod N) are valid signatures. Canonical signatures are those where 1 <= S <= N/2 - * - *
See https://github.com/bitcoin/bips/blob/master/bip-0062.mediawiki - * #Low_S_values_in_signatures + * ECDSA signatures are mutable: for a given (R, S) pair, both (R, S) and (R, N - S mod N) are + * valid. Canonical signatures satisfy 1 <= S <= N/2, where N is the curve order (SECP256K1N). + *
+ * Reference: + * https://github.com/bitcoin/bips/blob/master/bip-0062.mediawiki#Low_S_values_in_signatures + *
+ * For the TRON network, since the transaction ID does not include the signature and can still + * guarantee the transaction uniqueness, it is not necessary to strictly enforce signature + * canonicalization. Signature verification accepts both low-S and high-S forms. + *
+ * Note: While not enforced by the protocol, using low-S signatures is recommended to prevent + * signature malleability. */ - public static final BigInteger HALF_CURVE_ORDER; - private static final BigInteger SECP256K1N = - new BigInteger("fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141", 16); + private static final BigInteger SECP256K1N; private static final SecureRandom secureRandom; private static final long serialVersionUID = -728224901792295832L; @@ -89,6 +92,7 @@ public class ECKey implements Serializable, SignInterface { params.getN(), params.getH()); CURVE_SPEC = new ECParameterSpec(params.getCurve(), params.getG(), params.getN(), params.getH()); + SECP256K1N = params.getN(); HALF_CURVE_ORDER = params.getN().shiftRight(1); secureRandom = new SecureRandom(); } @@ -108,13 +112,14 @@ public class ECKey implements Serializable, SignInterface { private final Provider provider; // Transient because it's calculated on demand. - private transient byte[] pubKeyHash; - private transient byte[] nodeId; + private transient volatile byte[] pubKeyHash; + private transient volatile byte[] nodeId; /** * Generates an entirely new keypair. * - *
BouncyCastle will be used as the Java Security Provider + *
+ * BouncyCastle will be used as the Java Security Provider */ public ECKey() { this(secureRandom); @@ -123,7 +128,8 @@ public ECKey() { /** * Generate a new keypair using the given Java Security Provider. * - *
All private key operations will use the provider. + *
+ * All private key operations will use the provider. */ public ECKey(Provider provider, SecureRandom secureRandom) { this.provider = provider; @@ -147,8 +153,9 @@ public ECKey(Provider provider, SecureRandom secureRandom) { } /** - * Generates an entirely new keypair with the given {@link SecureRandom} object.
BouncyCastle - * will be used as the Java Security Provider + * Generates an entirely new keypair with the given {@link SecureRandom} object. + *
+ * BouncyCastle will be used as the Java Security Provider * * @param secureRandom - */ @@ -159,15 +166,22 @@ public ECKey(SecureRandom secureRandom) { /** * Pair a private key with a public EC point. * - *
All private key operations will use the provider. + *
+ * All private key operations will use the provider. */ public ECKey(byte[] key, boolean isPrivateKey) { if (isPrivateKey) { + if (!isValidPrivateKey(key)) { + throw new IllegalArgumentException("Invalid private key."); + } BigInteger pk = new BigInteger(1, key); this.privKey = privateKeyFromBigInteger(pk); this.pub = CURVE.getG().multiply(pk); } else { + if (!isValidPublicKey(key)) { + throw new IllegalArgumentException("Invalid public key."); + } this.privKey = null; this.pub = CURVE.getCurve().decodePoint(key); } @@ -183,31 +197,32 @@ public ECKey(Provider provider, @Nullable PrivateKey privKey, ECPoint pub) { throw new IllegalArgumentException( "Expected EC private key, given a private key object with" + " class " - + privKey.getClass().toString() + + + privKey.getClass() + " and algorithm " + privKey.getAlgorithm()); } if (pub == null) { - throw new IllegalArgumentException("Public key may not be null"); + throw new IllegalArgumentException("Public key should not be null"); } else { this.pub = pub; } } /** - * Pair a private key integer with a public EC point
BouncyCastle will be used as the Java - * Security Provider + * Pair a private key integer with a public EC point + *
+ * BouncyCastle will be used as the Java Security Provider */ public ECKey(@Nullable BigInteger priv, ECPoint pub) { this( TronCastleProvider.getInstance(), - privateKeyFromBigInteger(priv), - pub - ); + priv == null ? null : privateKeyFromBigInteger(priv), + pub); } - /* Convert a Java JCE ECPublicKey into a BouncyCastle ECPoint + /* + * Convert a Java JCE ECPublicKey into a BouncyCastle ECPoint */ private static ECPoint extractPublicKey(final ECPublicKey ecPublicKey) { final java.security.spec.ECPoint publicPointW = ecPublicKey.getW(); @@ -217,7 +232,8 @@ private static ECPoint extractPublicKey(final ECPublicKey ecPublicKey) { return CURVE.getCurve().createPoint(xCoord, yCoord); } - /* Test if a generic private key is an EC private key + /* + * Test if a generic private key is an EC private key * * it is not sufficient to check that privKey is a subtype of ECPrivateKey * as the SunPKCS11 Provider will return a generic PrivateKey instance @@ -228,20 +244,50 @@ private static boolean isECPrivateKey(PrivateKey privKey) { .equals("EC"); } - /* Convert a BigInteger into a PrivateKey object + /* + * Convert a BigInteger into a PrivateKey object */ private static PrivateKey privateKeyFromBigInteger(BigInteger priv) { - if (priv == null) { - return null; - } else { - try { - return ECKeyFactory - .getInstance(TronCastleProvider.getInstance()) - .generatePrivate(new ECPrivateKeySpec(priv, - CURVE_SPEC)); - } catch (InvalidKeySpecException ex) { - throw new AssertionError("Assumed correct key spec statically"); - } + if (!isValidPrivateKey(priv)) { + throw new IllegalArgumentException("Invalid private key."); + } + + try { + return ECKeyFactory + .getInstance(TronCastleProvider.getInstance()) + .generatePrivate(new ECPrivateKeySpec(priv, + CURVE_SPEC)); + } catch (InvalidKeySpecException ex) { + throw new AssertionError("Assumed correct key spec statically"); + } + } + + public static boolean isValidPrivateKey(byte[] keyBytes) { + if (ByteArray.isEmpty(keyBytes)) { + return false; + } + + BigInteger key = new BigInteger(1, keyBytes); + return key.compareTo(BigInteger.ONE) >= 0 && key.compareTo(CURVE.getN()) < 0; + } + + public static boolean isValidPrivateKey(BigInteger privateKey) { + if (privateKey == null) { + return false; + } + return privateKey.compareTo(BigInteger.ONE) >= 0 && privateKey.compareTo(CURVE.getN()) < 0; + } + + public static boolean isValidPublicKey(byte[] keyBytes) { + if (ByteArray.isEmpty(keyBytes)) { + return false; + } + + try { + ECPoint point = CURVE.getCurve().decodePoint(keyBytes); + return !point.isInfinity() && point.isValid(); + } catch (RuntimeException e) { + return false; } } @@ -276,6 +322,10 @@ public static ECPoint decompressPoint(ECPoint compressed) { * @return - */ public static ECKey fromPrivate(BigInteger privKey) { + if (!isValidPrivateKey(privKey)) { + throw new IllegalArgumentException("Invalid private key."); + } + return new ECKey(privKey, CURVE.getG().multiply(privKey)); } @@ -286,54 +336,12 @@ public static ECKey fromPrivate(BigInteger privKey) { * @return - */ public static ECKey fromPrivate(byte[] privKeyBytes) { - if (ByteArray.isEmpty(privKeyBytes)) { - return null; + if (!isValidPrivateKey(privKeyBytes)) { + throw new IllegalArgumentException("Invalid private key."); } return fromPrivate(new BigInteger(1, privKeyBytes)); } - /** - * Creates an ECKey that simply trusts the caller to ensure that point is really the result of - * multiplying the generator point by the private key. This is used to speed things up when you - * know you have the right values already. The compression state of pub will be preserved. - * - * @param priv - - * @param pub - - * @return - - */ - public static ECKey fromPrivateAndPrecalculatedPublic(BigInteger priv, - ECPoint pub) { - return new ECKey(priv, pub); - } - - /** - * Creates an ECKey that simply trusts the caller to ensure that point is really the result of - * multiplying the generator point by the private key. This is used to speed things up when you - * know you have the right values already. The compression state of the point will be preserved. - * - * @param priv - - * @param pub - - * @return - - */ - public static ECKey fromPrivateAndPrecalculatedPublic(byte[] priv, byte[] - pub) { - check(priv != null, "Private key must not be null"); - check(pub != null, "Public key must not be null"); - return new ECKey(new BigInteger(1, priv), CURVE.getCurve() - .decodePoint(pub)); - } - - /** - * Creates an ECKey that cannot be used for signing, only verifying signatures, from the given - * point. The compression state of pub will be preserved. - * - * @param pub - - * @return - - */ - public static ECKey fromPublicOnly(ECPoint pub) { - return new ECKey(null, pub); - } - /** * Creates an ECKey that cannot be used for signing, only verifying signatures, from the given * encoded point. The compression state of pub will be preserved. @@ -349,19 +357,19 @@ public static ECKey fromPublicOnly(byte[] pub) { * Returns public key bytes from the given private key. To convert a byte array into a BigInteger, * use new BigInteger(1, bytes); * - * @param privKey - + * @param privKey - * @param compressed - * @return - */ - public static byte[] publicKeyFromPrivate(BigInteger privKey, boolean - compressed) { + public static byte[] publicKeyFromPrivate(BigInteger privKey, boolean compressed) { ECPoint point = CURVE.getG().multiply(privKey); return point.getEncoded(compressed); } /** - * Compute the encoded X, Y coordinates of a public point.
This is the encoded public key - * without the leading byte. + * Compute the encoded X, Y coordinates of a public point. + *
+ * This is the encoded public key without the leading byte. * * @param pubPoint a public point * @return 64-byte X,Y point pair @@ -384,14 +392,13 @@ public static ECKey fromNodeId(byte[] nodeId) { return ECKey.fromPublicOnly(pubBytes); } - public static byte[] signatureToKeyBytes(byte[] messageHash, String - signatureBase64) throws SignatureException { + public static byte[] signatureToKeyBytes(byte[] messageHash, String signatureBase64) + throws SignatureException { byte[] signatureEncoded; try { signatureEncoded = Base64.decode(signatureBase64); } catch (RuntimeException e) { - // This is what you getData back from Bouncy Castle if base64 doesn't - // decode :( + // This is what you getData back from Bouncy Castle if base64 doesn't decode throw new SignatureException("Could not decode base64", e); } // Parse the signature bytes into r/s and the selector value. @@ -415,7 +422,7 @@ public static byte[] signatureToKeyBytes(byte[] messageHash, int header = sig.v; // The header byte: 0x1B = first key with even y, 0x1C = first key // with odd y, - // 0x1D = second key with even y, 0x1E = second key + // 0x1D = second key with even y, 0x1E = second key // with odd y if (header < 27 || header > 34) { throw new SignatureException("Header byte out of range: " + header); @@ -436,12 +443,12 @@ public static byte[] signatureToKeyBytes(byte[] messageHash, /** * Compute the address of the key that signed the given signature. * - * @param messageHash 32-byte hash of message + * @param messageHash 32-byte hash of message * @param signatureBase64 Base-64 encoded signature * @return 20-byte address */ - public static byte[] signatureToAddress(byte[] messageHash, String - signatureBase64) throws SignatureException { + public static byte[] signatureToAddress(byte[] messageHash, String signatureBase64) + throws SignatureException { return Hash.computeAddress(signatureToKeyBytes(messageHash, signatureBase64)); } @@ -450,24 +457,23 @@ public static byte[] signatureToAddress(byte[] messageHash, String * Compute the address of the key that signed the given signature. * * @param messageHash 32-byte hash of message - * @param sig - + * @param sig - * @return 20-byte address */ public static byte[] signatureToAddress(byte[] messageHash, - ECDSASignature sig) throws - SignatureException { + ECDSASignature sig) throws SignatureException { return Hash.computeAddress(signatureToKeyBytes(messageHash, sig)); } /** * Compute the key that signed the given signature. * - * @param messageHash 32-byte hash of message + * @param messageHash 32-byte hash of message * @param signatureBase64 Base-64 encoded signature * @return ECKey */ - public static ECKey signatureToKey(byte[] messageHash, String - signatureBase64) throws SignatureException { + public static ECKey signatureToKey(byte[] messageHash, String signatureBase64) + throws SignatureException { final byte[] keyBytes = signatureToKeyBytes(messageHash, signatureBase64); return ECKey.fromPublicOnly(keyBytes); @@ -493,25 +499,14 @@ public static boolean isPubKeyCanonical(byte[] pubkey) { } /** - *
Given the components of a signature and a selector value, recover and return the public key - * that generated the signature according to the algorithm in SEC1v2 section 4.1.6.
- * - *
The recId is an index from 0 to 3 which indicates which of the 4 possible allKeys is - * the - * correct one. Because the key recovery operation yields multiple potential allKeys, the correct - * key must either be stored alongside the signature, or you must be willing to try each recId in - * turn until you find one that outputs the key you are expecting.
- * - *
If this method returns null it means recovery was not possible and recId should be - * iterated.
+ * Recover the public key from a signature, per SEC1v2 section 4.1.6. * - *
Given the above two points, a correct usage of this method is inside a for loop from 0 - * to 3, and if the output is null OR a key that is not the one you expect, you try again with the - * next recId.
+ *recId (0–3) selects which of the candidate keys to return. Iterate recId in a loop and + * retry if the result is null or not the expected key. * - * @param recId Which possible key to recover. - * @param sig the R and S components of the signature, wrapped. - * @param messageHash Hash of the data that was signed. + * @param recId which possible key to recover + * @param sig the R and S components of the signature, wrapped + * @param messageHash hash of the signed data * @return 65-byte encoded public key */ @Nullable @@ -521,26 +516,26 @@ public static byte[] recoverPubBytesFromSignature(int recId, check(sig.r.signum() >= 0, "r must be positive"); check(sig.s.signum() >= 0, "s must be positive"); check(messageHash != null, "messageHash must not be null"); - // 1.0 For j from 0 to h (h == recId here and the loop is outside + // 1.0 For j from 0 to h (h == recId here and the loop is outside // this function) - // 1.1 Let x = r + jn - BigInteger n = CURVE.getN(); // Curve order. + // 1.1 Let x = r + jn + BigInteger n = CURVE.getN(); // Curve order. BigInteger i = BigInteger.valueOf((long) recId / 2); BigInteger x = sig.r.add(i.multiply(n)); - // 1.2. Convert the integer x to an octet string X of length mlen + // 1.2. Convert the integer x to an octet string X of length mlen // using the conversion routine - // specified in Section 2.3.7, where mlen = ⌈(log2 p)/8⌉ or + // specified in Section 2.3.7, where mlen = ⌈(log2 p)/8⌉ or // mlen = ⌈m/8⌉. - // 1.3. Convert the octet string (16 set binary digits)||X to an + // 1.3. Convert the octet string (16 set binary digits)||X to an // elliptic curve point R using the - // conversion routine specified in Section 2.3.4. If this + // conversion routine specified in Section 2.3.4. If this // conversion routine outputs “invalid”, then - // do another iteration of Step 1. + // do another iteration of Step 1. // // More concisely, what these points mean is to use X as a compressed // public key. ECCurve.Fp curve = (ECCurve.Fp) CURVE.getCurve(); - BigInteger prime = curve.getQ(); // Bouncy Castle is not consistent + BigInteger prime = curve.getQ(); // Bouncy Castle is not consistent // about the letter it uses for the prime. if (x.compareTo(prime) >= 0) { // Cannot have point co-ordinates larger than this as everything @@ -551,22 +546,22 @@ public static byte[] recoverPubBytesFromSignature(int recId, // y-coord as there are two possibilities. // So it's encoded in the recId. ECPoint R = decompressKey(x, (recId & 1) == 1); - // 1.4. If nR != point at infinity, then do another iteration of + // 1.4. If nR != point at infinity, then do another iteration of // Step 1 (callers responsibility). if (!R.multiply(n).isInfinity()) { return null; } - // 1.5. Compute e from M using Steps 2 and 3 of ECDSA signature + // 1.5. Compute e from M using Steps 2 and 3 of ECDSA signature // verification. BigInteger e = new BigInteger(1, messageHash); - // 1.6. For k from 1 to 2 do the following. (loop is outside this + // 1.6. For k from 1 to 2 do the following. (loop is outside this // function via iterating recId) - // 1.6.1. Compute a candidate public key as: - // Q = mi(r) * (sR - eG) + // 1.6.1. Compute a candidate public key as: + // Q = mi(r) * (sR - eG) // // Where mi(x) is the modular multiplicative inverse. We transform // this into the following: - // Q = (mi(r) * s ** R) + (mi(r) * -e ** G) + // Q = (mi(r) * s ** R) + (mi(r) * -e ** G) // Where -e is the modular additive inverse of e, that is z such that // z + e = 0 (mod n). In the above equation // ** is point multiplication and + is point addition (the EC group @@ -586,8 +581,8 @@ public static byte[] recoverPubBytesFromSignature(int recId, } /** - * @param recId Which possible key to recover. - * @param sig the R and S components of the signature, wrapped. + * @param recId Which possible key to recover. + * @param sig the R and S components of the signature, wrapped. * @param messageHash Hash of the data that was signed. * @return 20-byte address */ @@ -604,8 +599,8 @@ public static byte[] recoverAddressFromSignature(int recId, } /** - * @param recId Which possible key to recover. - * @param sig the R and S components of the signature, wrapped. + * @param recId Which possible key to recover. + * @param sig the R and S components of the signature, wrapped. * @param messageHash Hash of the data that was signed. * @return ECKey */ @@ -624,7 +619,7 @@ public static ECKey recoverFromSignature(int recId, ECDSASignature sig, /** * Decompress a compressed public key (x co-ord and low-bit of y-coord). * - * @param xBN - + * @param xBN - * @param yBit - * @return - */ @@ -654,8 +649,8 @@ public boolean isPubKeyOnly() { } /** - * Returns true if this key has access to private key bytes. Does the opposite of {@link - * #isPubKeyOnly()}. + * Returns true if this key has access to private key bytes. Does the opposite of + * {@link #isPubKeyOnly()}. * * @return - */ @@ -672,7 +667,7 @@ public byte[] getAddress() { if (pubKeyHash == null) { pubKeyHash = Hash.computeAddress(this.pub); } - return pubKeyHash; + return Arrays.copyOf(pubKeyHash, pubKeyHash.length); } @Override @@ -694,10 +689,9 @@ public byte[] getNodeId() { if (nodeId == null) { nodeId = pubBytesWithoutFormat(this.pub); } - return nodeId; + return Arrays.copyOf(nodeId, nodeId.length); } - @Override public byte[] getPrivateKey() { return getPrivKeyBytes(); @@ -739,25 +733,7 @@ public BigInteger getPrivKey() { } public String toString() { - StringBuilder b = new StringBuilder(); - b.append("pub:").append(Hex.toHexString(pub.getEncoded(false))); - return b.toString(); - } - - /** - * Produce a string rendering of the ECKey INCLUDING the private key. Unless you absolutely need - * the private key it is better for security reasons to just use toString(). - * - * @return - - */ - public String toStringWithPrivate() { - StringBuilder b = new StringBuilder(); - b.append(toString()); - if (privKey != null && privKey instanceof BCECPrivateKey) { - b.append(" priv:").append(Hex.toHexString(((BCECPrivateKey) - privKey).getD().toByteArray())); - } - return b.toString(); + return "pub:" + Hex.toHexString(pub.getEncoded(false)); } /** @@ -777,10 +753,9 @@ public ECDSASignature doSign(byte[] input) { throw new MissingPrivateKeyException(); } if (privKey instanceof BCECPrivateKey) { - ECDSASigner signer = new ECDSASigner(new HMacDSAKCalculator(new - SHA256Digest())); - ECPrivateKeyParameters privKeyParams = new ECPrivateKeyParameters - (((BCECPrivateKey) privKey).getD(), CURVE); + ECDSASigner signer = new ECDSASigner(new HMacDSAKCalculator(new SHA256Digest())); + ECPrivateKeyParameters privKeyParams = new ECPrivateKeyParameters( + ((BCECPrivateKey) privKey).getD(), CURVE); signer.init(true, privKeyParams); BigInteger[] components = signer.generateSignature(input); return new ECDSASignature(components[0], components[1]) @@ -818,7 +793,6 @@ public ECDSASignature sign(byte[] messageHash) { return sig; } - /** * Returns true if this pubkey is canonical, i.e. the correct length taking into account * compression. @@ -913,8 +887,7 @@ private static ECDSASignature fromComponents(byte[] r, byte[] s) { * @param v - * @return - */ - public static ECDSASignature fromComponents(byte[] r, byte[] s, byte - v) { + public static ECDSASignature fromComponents(byte[] r, byte[] s, byte v) { ECDSASignature signature = fromComponents(r, s); signature.v = v; return signature; @@ -940,7 +913,6 @@ public static boolean validateComponents(BigInteger r, BigInteger s, return BIUtil.isLessThan(s, SECP256K1N); } - public boolean validateComponents() { return validateComponents(r, s, v); } @@ -951,10 +923,10 @@ public ECDSASignature toCanonicalised() { // exist on that curve. If S is in the upper // half of the number of valid points, then bring it back to // the lower half. Otherwise, imagine that - // N = 10 - // s = 8, so (-8 % 10 == 2) thus both (r, 8) and (r, 2) + // N = 10 + // s = 8, so (-8 % 10 == 2) thus both (r, 8) and (r, 2) // are valid solutions. - // 10 - 8 == 2, giving us always the latter solution, + // 10 - 8 == 2, giving us always the latter solution, // which is canonical. return new ECDSASignature(r, CURVE.getN().subtract(s)); } else { @@ -966,7 +938,7 @@ public ECDSASignature toCanonicalised() { * @return - */ public String toBase64() { - byte[] sigData = new byte[65]; // 1 header + 32 bytes for R + 32 + byte[] sigData = new byte[65]; // 1 header + 32 bytes for R + 32 // bytes for S sigData[0] = v; System.arraycopy(ByteUtil.bigIntegerToBytes(this.r, 32), 0, sigData, 1, 32); @@ -974,7 +946,6 @@ public String toBase64() { return new String(Base64.encode(sigData), Charset.forName("UTF-8")); } - public byte[] toByteArray() { final byte fixedV = this.v >= 27 ? (byte) (this.v - 27) diff --git a/crypto/src/main/java/org/tron/common/crypto/Rsv.java b/crypto/src/main/java/org/tron/common/crypto/Rsv.java index 15c8498e836..e37be7d8be5 100644 --- a/crypto/src/main/java/org/tron/common/crypto/Rsv.java +++ b/crypto/src/main/java/org/tron/common/crypto/Rsv.java @@ -15,6 +15,10 @@ public class Rsv { public static Rsv fromSignature(byte[] sign) { + if (sign == null || sign.length < 65) { + throw new IllegalArgumentException( + "Invalid signature length: " + (sign == null ? "null" : sign.length)); + } byte[] r = Arrays.copyOfRange(sign, 0, 32); byte[] s = Arrays.copyOfRange(sign, 32, 64); byte v = sign[64]; diff --git a/crypto/src/main/java/org/tron/common/crypto/SignUtils.java b/crypto/src/main/java/org/tron/common/crypto/SignUtils.java index b921d548e8b..2c2519dd931 100644 --- a/crypto/src/main/java/org/tron/common/crypto/SignUtils.java +++ b/crypto/src/main/java/org/tron/common/crypto/SignUtils.java @@ -23,6 +23,13 @@ public static SignInterface fromPrivate(byte[] privKeyBytes, boolean isECKeyCryp return SM2.fromPrivate(privKeyBytes); } + public static boolean isValidPrivateKey(byte[] privKeyBytes, boolean isECKeyCryptoEngine) { + if (isECKeyCryptoEngine) { + return ECKey.isValidPrivateKey(privKeyBytes); + } + return SM2.isValidPrivateKey(privKeyBytes); + } + public static byte[] signatureToAddress( byte[] messageHash, String signatureBase64, boolean isECKeyCryptoEngine) throws SignatureException { diff --git a/crypto/src/main/java/org/tron/common/crypto/sm2/SM2.java b/crypto/src/main/java/org/tron/common/crypto/sm2/SM2.java index b1d349efad3..f3a0254f075 100644 --- a/crypto/src/main/java/org/tron/common/crypto/sm2/SM2.java +++ b/crypto/src/main/java/org/tron/common/crypto/sm2/SM2.java @@ -4,15 +4,14 @@ import static org.tron.common.utils.BIUtil.isLessThan; import static org.tron.common.utils.ByteUtil.bigIntegerToBytes; -import java.io.IOException; import java.io.Serializable; +import java.io.IOException; import java.math.BigInteger; -import java.nio.charset.Charset; +import java.nio.charset.StandardCharsets; import java.security.PrivateKey; import java.security.SecureRandom; import java.security.SignatureException; import java.security.interfaces.ECPrivateKey; -import java.security.interfaces.ECPublicKey; import java.security.spec.InvalidKeySpecException; import java.util.Arrays; import java.util.Objects; @@ -20,7 +19,8 @@ import lombok.extern.slf4j.Slf4j; import org.bouncycastle.asn1.ASN1InputStream; import org.bouncycastle.asn1.ASN1Integer; -import org.bouncycastle.asn1.DLSequence; +import org.bouncycastle.asn1.ASN1Primitive; +import org.bouncycastle.asn1.ASN1Sequence; import org.bouncycastle.asn1.x9.X9IntegerConverter; import org.bouncycastle.crypto.AsymmetricCipherKeyPair; import org.bouncycastle.crypto.generators.ECKeyPairGenerator; @@ -41,6 +41,7 @@ import org.tron.common.crypto.SignatureInterface; import org.tron.common.crypto.jce.ECKeyFactory; import org.tron.common.crypto.jce.TronCastleProvider; +import org.tron.common.utils.BIUtil; import org.tron.common.utils.ByteArray; import org.tron.common.utils.ByteUtil; @@ -50,63 +51,62 @@ @Slf4j(topic = "crypto") public class SM2 implements Serializable, SignInterface { - private static BigInteger SM2_N = new BigInteger( + private static final BigInteger SM2_N = new BigInteger( "FFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFF7203DF6B21C6052B53BBF40939D54123", 16); - private static BigInteger SM2_P = new BigInteger( + private static final BigInteger SM2_P = new BigInteger( "FFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000000FFFFFFFFFFFFFFFF", 16); - private static BigInteger SM2_A = new BigInteger( + private static final BigInteger SM2_A = new BigInteger( "FFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000000FFFFFFFFFFFFFFFC", 16); - private static BigInteger SM2_B = new BigInteger( + private static final BigInteger SM2_B = new BigInteger( "28E9FA9E9D9F5E344D5A9E4BCF6509A7F39789F515AB8F92DDBCBD414D940E93", 16); - private static BigInteger SM2_GX = new BigInteger( + private static final BigInteger SM2_GX = new BigInteger( "32C4AE2C1F1981195F9904466A39C9948FE30BBFF2660BE1715A4589334C74C7", 16); - private static BigInteger SM2_GY = new BigInteger( + private static final BigInteger SM2_GY = new BigInteger( "BC3736A2F4F6779C59BDCEE36B692153D0A9877CC62A474002DF32E52139F0A0", 16); - private static ECDomainParameters ecc_param; - private static ECParameterSpec ecc_spec; - private static ECCurve.Fp curve; - private static ECPoint ecc_point_g; + private static final ECDomainParameters eccParam; + private static final ECParameterSpec eccSpec; + private static final ECCurve.Fp curve; + private static final ECPoint eccPointG; private static final SecureRandom secureRandom; - static { secureRandom = new SecureRandom(); curve = new ECCurve.Fp(SM2_P, SM2_A, SM2_B, null, null); - ecc_point_g = curve.createPoint(SM2_GX, SM2_GY); - ecc_param = new ECDomainParameters(curve, ecc_point_g, SM2_N); - ecc_spec = new ECParameterSpec(curve, ecc_point_g, SM2_N); + eccPointG = curve.createPoint(SM2_GX, SM2_GY); + eccParam = new ECDomainParameters(curve, eccPointG, SM2_N); + eccSpec = new ECParameterSpec(curve, eccPointG, SM2_N); } protected final ECPoint pub; private final PrivateKey privKey; - // Transient because it's calculated on demand. - private transient byte[] pubKeyHash; - private transient byte[] nodeId; - + private transient volatile byte[] pubKeyHash; + private transient volatile byte[] nodeId; public SM2() { this(secureRandom); } + /** * Generates an entirely new keypair. * - *
BouncyCastle will be used as the Java Security Provider + *
+ * BouncyCastle will be used as the Java Security Provider */ - /** * Generate a new keypair using the given Java Security Provider. * - *
All private key operations will use the provider. + *
+ * All private key operations will use the provider. */ public SM2(SecureRandom secureRandom) { - ECKeyGenerationParameters ecKeyGenerationParameters = new ECKeyGenerationParameters(ecc_param, + ECKeyGenerationParameters ecKeyGenerationParameters = new ECKeyGenerationParameters(eccParam, secureRandom); ECKeyPairGenerator keyPairGenerator = new ECKeyPairGenerator(); keyPairGenerator.init(ecKeyGenerationParameters); @@ -121,37 +121,48 @@ public SM2(SecureRandom secureRandom) { public SM2(byte[] key, boolean isPrivateKey) { if (isPrivateKey) { + if (!isValidPrivateKey(key)) { + throw new IllegalArgumentException("Invalid private key in SM2."); + } + BigInteger pk = new BigInteger(1, key); this.privKey = privateKeyFromBigInteger(pk); - this.pub = ecc_param.getG().multiply(pk); + this.pub = eccParam.getG().multiply(pk); } else { + if (ByteArray.isEmpty(key)) { + throw new IllegalArgumentException("Empty public key in SM2."); + } + + ECPoint point = eccParam.getCurve().decodePoint(key); + if (point.isInfinity() || !point.isValid()) { + throw new IllegalArgumentException("Public key is not a valid point on SM2 curve."); + } this.privKey = null; - this.pub = ecc_param.getCurve().decodePoint(key); + this.pub = point; } } - /** * Pair a private key with a public EC point. * - *
All private key operations will use the provider. + *
+ * All private key operations will use the provider. */ public SM2(@Nullable PrivateKey privKey, ECPoint pub) { - if (privKey == null || isECPrivateKey(privKey)) { this.privKey = privKey; } else { throw new IllegalArgumentException( "Expected EC private key, given a private key object with" + " class " - + privKey.getClass().toString() + + + privKey.getClass() + " and algorithm " + privKey.getAlgorithm()); } if (pub == null) { - throw new IllegalArgumentException("Public key may not be null"); + throw new IllegalArgumentException("Public key should not be null"); } else { this.pub = pub; } @@ -162,30 +173,30 @@ public SM2(@Nullable PrivateKey privKey, ECPoint pub) { */ public SM2(@Nullable BigInteger priv, ECPoint pub) { this( - privateKeyFromBigInteger(priv), - pub - ); + priv == null ? null : privateKeyFromBigInteger(priv), + pub); } /** * Convert a BigInteger into a PrivateKey object */ private static PrivateKey privateKeyFromBigInteger(BigInteger priv) { - if (priv == null) { - return null; - } else { - try { - return ECKeyFactory - .getInstance(TronCastleProvider.getInstance()) - .generatePrivate(new ECPrivateKeySpec(priv, - ecc_spec)); - } catch (InvalidKeySpecException ex) { - throw new AssertionError("Assumed correct key spec statically"); - } + if (!isValidPrivateKey(priv)) { + throw new IllegalArgumentException("Invalid private key in SM2."); + } + + try { + return ECKeyFactory + .getInstance(TronCastleProvider.getInstance()) + .generatePrivate(new ECPrivateKeySpec(priv, + eccSpec)); + } catch (InvalidKeySpecException ex) { + throw new AssertionError("Assumed correct key spec statically"); } } - /* Test if a generic private key is an EC private key + /* + * Test if a generic private key is an EC private key * * it is not sufficient to check that privKey is a subtype of ECPrivateKey * as the SunPKCS11 Provider will return a generic PrivateKey instance @@ -196,19 +207,9 @@ private static boolean isECPrivateKey(PrivateKey privKey) { .equals("EC"); } - /* Convert a Java JCE ECPublicKey into a BouncyCastle ECPoint - */ - private static ECPoint extractPublicKey(final ECPublicKey ecPublicKey) { - final java.security.spec.ECPoint publicPointW = ecPublicKey.getW(); - final BigInteger xCoord = publicPointW.getAffineX(); - final BigInteger yCoord = publicPointW.getAffineY(); - - return ecc_param.getCurve().createPoint(xCoord, yCoord); - } - - /** - * Utility for compressing an elliptic curve point. Returns the same point if it's already + * Utility for compressing an elliptic curve point. Returns the same point if + * it's already * compressed. See the ECKey class docs for a discussion of point compression. * * @param uncompressed - @@ -216,11 +217,12 @@ private static ECPoint extractPublicKey(final ECPublicKey ecPublicKey) { * @deprecated per-point compression property will be removed in Bouncy Castle */ public static ECPoint compressPoint(ECPoint uncompressed) { - return ecc_param.getCurve().decodePoint(uncompressed.getEncoded(true)); + return eccParam.getCurve().decodePoint(uncompressed.getEncoded(true)); } /** - * Utility for decompressing an elliptic curve point. Returns the same point if it's already + * Utility for decompressing an elliptic curve point. Returns the same point if + * it's already * compressed. See the ECKey class docs for a discussion of point compression. * * @param compressed - @@ -228,7 +230,7 @@ public static ECPoint compressPoint(ECPoint uncompressed) { * @deprecated per-point compression property will be removed in Bouncy Castle */ public static ECPoint decompressPoint(ECPoint compressed) { - return ecc_param.getCurve().decodePoint(compressed.getEncoded(false)); + return eccParam.getCurve().decodePoint(compressed.getEncoded(false)); } /** @@ -238,7 +240,11 @@ public static ECPoint decompressPoint(ECPoint compressed) { * @return - */ public static SM2 fromPrivate(BigInteger privKey) { - return new SM2(privKey, ecc_param.getG().multiply(privKey)); + if (!isValidPrivateKey(privKey)) { + throw new IllegalArgumentException("Invalid private key in SM2."); + } + + return new SM2(privKey, eccParam.getG().multiply(privKey)); } /** @@ -248,81 +254,59 @@ public static SM2 fromPrivate(BigInteger privKey) { * @return - */ public static SM2 fromPrivate(byte[] privKeyBytes) { - if (ByteArray.isEmpty(privKeyBytes)) { - return null; + if (!isValidPrivateKey(privKeyBytes)) { + throw new IllegalArgumentException("Invalid private key in SM2."); } + return fromPrivate(new BigInteger(1, privKeyBytes)); } - /** - * Creates an SM2 that simply trusts the caller to ensure that point is really the result of - * multiplying the generator point by the private key. This is used to speed things up when you - * know you have the right values already. The compression state of pub will be preserved. - * - * @param priv - - * @param pub - - * @return - - */ - public static SM2 fromPrivateAndPrecalculatedPublic(BigInteger priv, - ECPoint pub) { - return new SM2(priv, pub); - } + public static boolean isValidPrivateKey(byte[] keyBytes) { + if (ByteArray.isEmpty(keyBytes)) { + return false; + } - /** - * Creates an SM2 that simply trusts the caller to ensure that point is really the result of - * multiplying the generator point by the private key. This is used to speed things up when you - * know you have the right values already. The compression state of the point will be preserved. - * - * @param priv - - * @param pub - - * @return - - */ - public static SM2 fromPrivateAndPrecalculatedPublic(byte[] priv, byte[] - pub) { - check(priv != null, "Private key must not be null"); - check(pub != null, "Public key must not be null"); - return new SM2(new BigInteger(1, priv), ecc_param.getCurve() - .decodePoint(pub)); + BigInteger key = new BigInteger(1, keyBytes); + return key.compareTo(BigInteger.ONE) >= 0 && key.compareTo(SM2_N) < 0; } - /** - * Creates an SM2 that cannot be used for signing, only verifying signatures, from the given - * point. The compression state of pub will be preserved. - * - * @param pub - - * @return - - */ - public static SM2 fromPublicOnly(ECPoint pub) { - return new SM2((PrivateKey) null, pub); + public static boolean isValidPrivateKey(BigInteger privateKey) { + if (privateKey == null) { + return false; + } + return privateKey.compareTo(BigInteger.ONE) >= 0 && privateKey.compareTo(SM2_N) < 0; } /** - * Creates an SM2 that cannot be used for signing, only verifying signatures, from the given + * Creates an SM2 that cannot be used for signing, only verifying signatures, + * from the given * encoded point. The compression state of pub will be preserved. * * @param pub - * @return - */ public static SM2 fromPublicOnly(byte[] pub) { - return new SM2((PrivateKey) null, ecc_param.getCurve().decodePoint(pub)); + return new SM2((PrivateKey) null, eccParam.getCurve().decodePoint(pub)); } /** - * Returns public key bytes from the given private key. To convert a byte array into a BigInteger, + * Returns public key bytes from the given private key. To convert a byte array + * into a BigInteger, * use new BigInteger(1, bytes); * - * @param privKey - + * @param privKey - * @param compressed - * @return - */ - public static byte[] publicKeyFromPrivate(BigInteger privKey, boolean - compressed) { - ECPoint point = ecc_param.getG().multiply(privKey); + public static byte[] publicKeyFromPrivate(BigInteger privKey, boolean compressed) { + ECPoint point = eccParam.getG().multiply(privKey); return point.getEncoded(compressed); } /** - * Compute the encoded X, Y coordinates of a public point.
This is the encoded public key + * Compute the encoded X, Y coordinates of a public point. + *
+ * This is the encoded public key * without the leading byte. * * @param pubPoint a public point @@ -339,6 +323,9 @@ public static byte[] pubBytesWithoutFormat(ECPoint pubPoint) { * @param nodeId a 64-byte X,Y point pair */ public static SM2 fromNodeId(byte[] nodeId) { + if (nodeId == null) { + throw new IllegalArgumentException("Node ID cannot be null"); + } check(nodeId.length == 64, "Expected a 64 byte node id"); byte[] pubBytes = new byte[65]; System.arraycopy(nodeId, 0, pubBytes, 1, nodeId.length); @@ -346,8 +333,10 @@ public static SM2 fromNodeId(byte[] nodeId) { return SM2.fromPublicOnly(pubBytes); } - public static byte[] signatureToKeyBytes(byte[] messageHash, String - signatureBase64) throws SignatureException { + public static byte[] signatureToKeyBytes(byte[] messageHash, String signatureBase64) throws SignatureException { + if (messageHash == null || signatureBase64 == null) { + throw new SignatureException("Message hash or signature cannot be null"); + } byte[] signatureEncoded; try { signatureEncoded = Base64.decode(signatureBase64); @@ -371,14 +360,16 @@ public static byte[] signatureToKeyBytes(byte[] messageHash, String } public static byte[] signatureToKeyBytes(byte[] messageHash, - SM2Signature sig) throws - SignatureException { + SM2Signature sig) throws SignatureException { + if (messageHash == null || sig == null) { + throw new SignatureException("Message hash or signature cannot be null"); + } check(messageHash.length == 32, "messageHash argument has length " + messageHash.length); int header = sig.v; // The header byte: 0x1B = first key with even y, 0x1C = first key // with odd y, - // 0x1D = second key with even y, 0x1E = second key + // 0x1D = second key with even y, 0x1E = second key // with odd y if (header < 27 || header > 34) { throw new SignatureException("Header byte out of range: " + header); @@ -396,7 +387,6 @@ public static byte[] signatureToKeyBytes(byte[] messageHash, return key; } - public byte[] hash(byte[] message) { SM2Signer signer = this.getSM2SignerForHash(); return signer.generateSM3Hash(message); @@ -427,19 +417,17 @@ public byte[] getAddress() { if (pubKeyHash == null) { pubKeyHash = computeAddress(this.pub); } - return pubKeyHash; + return Arrays.copyOf(pubKeyHash, pubKeyHash.length); } - /** * Compute the address of the key that signed the given signature. * - * @param messageHash 32-byte hash of message + * @param messageHash 32-byte hash of message * @param signatureBase64 Base-64 encoded signature * @return 20-byte address */ - public static byte[] signatureToAddress(byte[] messageHash, String - signatureBase64) throws SignatureException { + public static byte[] signatureToAddress(byte[] messageHash, String signatureBase64) throws SignatureException { return computeAddress(signatureToKeyBytes(messageHash, signatureBase64)); } @@ -448,24 +436,22 @@ public static byte[] signatureToAddress(byte[] messageHash, String * Compute the address of the key that signed the given signature. * * @param messageHash 32-byte hash of message - * @param sig - + * @param sig - * @return 20-byte address */ public static byte[] signatureToAddress(byte[] messageHash, - SM2Signature sig) throws - SignatureException { + SM2Signature sig) throws SignatureException { return computeAddress(signatureToKeyBytes(messageHash, sig)); } /** * Compute the key that signed the given signature. * - * @param messageHash 32-byte hash of message + * @param messageHash 32-byte hash of message * @param signatureBase64 Base-64 encoded signature * @return ECKey */ - public static SM2 signatureToKey(byte[] messageHash, String - signatureBase64) throws SignatureException { + public static SM2 signatureToKey(byte[] messageHash, String signatureBase64) throws SignatureException { final byte[] keyBytes = signatureToKeyBytes(messageHash, signatureBase64); return fromPublicOnly(keyBytes); @@ -475,54 +461,39 @@ public static SM2 signatureToKey(byte[] messageHash, String * Compute the key that signed the given signature. * * @param messageHash 32-byte hash of message - * @param sig - + * @param sig - * @return ECKey */ - public static SM2 signatureToKey(byte[] messageHash, SM2Signature - sig) throws SignatureException { + public static SM2 signatureToKey(byte[] messageHash, SM2Signature sig) throws SignatureException { final byte[] keyBytes = signatureToKeyBytes(messageHash, sig); return fromPublicOnly(keyBytes); } /** - * Takes the SM3 hash (32 bytes) of data and returns the SM2 signature which including the v + * Takes the SM3 hash (32 bytes) of data and returns the SM2 signature which + * including the v * * @param messageHash - * @return - * @throws IllegalStateException if this ECKey does not have the private part. */ public SM2Signature sign(byte[] messageHash) { - if (messageHash.length != 32) { + if (ByteArray.isEmpty(messageHash) || messageHash.length != 32) { throw new IllegalArgumentException("Expected 32 byte input to " + - "SM2 signature, not " + messageHash.length); + "SM2 signature, not " + (messageHash == null ? "null" : messageHash.length)); } // No decryption of private key required. SM2Signer signer = getSigner(); BigInteger[] componets = signer.generateHashSignature(messageHash); SM2Signature sig = new SM2Signature(componets[0], componets[1]); - // Now we have to work backwards to figure out the recId needed to - // recover the signature. - int recId = -1; - byte[] thisKey = this.pub.getEncoded(/* compressed */ false); - for (int i = 0; i < 4; i++) { - byte[] k = recoverPubBytesFromSignature(i, sig, messageHash); - if (k != null && Arrays.equals(k, thisKey)) { - recId = i; - break; - } - } - if (recId == -1) { - throw new RuntimeException("Could not construct a recoverable key" + - ". This should never happen."); - } - sig.v = (byte) (recId + 27); + sig.v = (byte) (findRecId(sig, messageHash) + 27); return sig; } /** - * Signs the given hash and returns the R and S components as BigIntegers and putData them in - * SM2Signature + * Signs the given hash and returns the R and S components as BigIntegers and + * putData them in SM2Signature * * @param input to sign * @return SM2Signature signature that contains the R and S components @@ -547,37 +518,22 @@ public byte[] Base64toBytes(String signature) { */ public SM2Signature signMessage(byte[] message, @Nullable String userID) { SM2Signature sig = signMsg(message, userID); - // Now we have to work backwards to figure out the recId needed to - // recover the signature. - int recId = -1; - byte[] thisKey = this.pub.getEncoded(/* compressed */ false); SM2Signer signer = getSigner(); byte[] messageHash = signer.generateSM3Hash(message); - for (int i = 0; i < 4; i++) { - byte[] k = recoverPubBytesFromSignature(i, sig, messageHash); - if (k != null && Arrays.equals(k, thisKey)) { - recId = i; - break; - } - } - if (recId == -1) { - throw new RuntimeException("Could not construct a recoverable key" + - ". This should never happen."); - } - sig.v = (byte) (recId + 27); + sig.v = (byte) (findRecId(sig, messageHash) + 27); return sig; } /** - * Signs the given hash and returns the R and S components as BigIntegers and putData them in - * SM2Signature + * Signs the given hash and returns the R and S components as BigIntegers and + * putData them in SM2Signature * * @param msg to sign * @return SM2Signature signature that contains the R and S components */ public SM2Signature signMsg(byte[] msg, @Nullable String userID) { - if (null == msg) { + if (msg == null) { throw new IllegalArgumentException("Expected signature message of " + "SM2 is null"); } @@ -587,10 +543,22 @@ public SM2Signature signMsg(byte[] msg, @Nullable String userID) { return new SM2Signature(componets[0], componets[1]); } + private int findRecId(SM2Signature sig, byte[] messageHash) { + byte[] thisKey = this.pub.getEncoded(/* compressed */ false); + for (int i = 0; i < 4; i++) { + byte[] k = recoverPubBytesFromSignature(i, sig, messageHash); + if (k != null && Arrays.equals(k, thisKey)) { + return i; + } + } + throw new RuntimeException("Could not construct a recoverable key" + + ". This should never happen."); + } + private SM2Signer getSigner() { SM2Signer signer = new SM2Signer(); BigInteger d = getPrivKey(); - ECPrivateKeyParameters privateKeyParameters = new ECPrivateKeyParameters(d, ecc_param); + ECPrivateKeyParameters privateKeyParameters = new ECPrivateKeyParameters(d, eccParam); signer.init(true, privateKeyParameters); return signer; } @@ -600,49 +568,51 @@ private SM2Signer getSigner() { */ public SM2Signer getSM2SignerForHash() { SM2Signer signer = new SM2Signer(); - ECPublicKeyParameters publicKeyParameters = new ECPublicKeyParameters(pub, ecc_param); + ECPublicKeyParameters publicKeyParameters = new ECPublicKeyParameters(pub, eccParam); signer.init(false, publicKeyParameters); return signer; } - /** - *
Given the components of a signature and a selector value, recover and return the public key - * that generated the signature + *
+ * Given the components of a signature and a selector value, recover and return + * the public key that generated the signature */ @Nullable public static byte[] recoverPubBytesFromSignature(int recId, SM2Signature sig, byte[] messageHash) { - check(recId >= 0, "recId must be positive"); - check(sig.r.signum() >= 0, "r must be positive"); - check(sig.s.signum() >= 0, "s must be positive"); - check(messageHash != null, "messageHash must not be null"); - // 1.0 For j from 0 to h (h == recId here and the loop is outside + if (sig == null || messageHash == null) { + throw new IllegalArgumentException("Signature or message hash cannot be null"); + } + check(recId >= 0 && recId <= 3, "recId must be in range [0, 3]"); + check(sig.r != null && sig.r.signum() > 0 && BIUtil.isLessThan(sig.r, SM2_N), + "r must be in range (0, n)"); + check(sig.s != null && sig.s.signum() > 0 && BIUtil.isLessThan(sig.s, SM2_N), + "s must be in range (0, n)"); + check(messageHash.length == 32, "messageHash must be 32 bytes"); + // 1.0 For j from 0 to h (h == recId here and the loop is outside // this function) - // 1.1 Let x = r + jn - BigInteger n = ecc_param.getN(); // Curve order. + // 1.1 Let x = r + jn + BigInteger n = eccParam.getN(); // Curve order. BigInteger prime = curve.getQ(); BigInteger i = BigInteger.valueOf((long) recId / 2); BigInteger e = new BigInteger(1, messageHash); - BigInteger x = sig.r.subtract(e).mod(n); // r = (x + e) mod n + BigInteger x = sig.r.subtract(e).mod(n); // r = (x + e) mod n x = x.add(i.multiply(n)); - // 1.2. Convert the integer x to an octet string X of length mlen + // 1.2. Convert the integer x to an octet string X of length mlen // using the conversion routine - // specified in Section 2.3.7, where mlen = ⌈(log2 p)/8⌉ or + // specified in Section 2.3.7, where mlen = ⌈(log2 p)/8⌉ or // mlen = ⌈m/8⌉. - // 1.3. Convert the octet string (16 set binary digits)||X to an + // 1.3. Convert the octet string (16 set binary digits)||X to an // elliptic curve point R using the - // conversion routine specified in Section 2.3.4. If this + // conversion routine specified in Section 2.3.4. If this // conversion routine outputs “invalid”, then - // do another iteration of Step 1. + // do another iteration of Step 1. // // More concisely, what these points mean is to use X as a compressed // public key. - ECCurve.Fp curve = (ECCurve.Fp) ecc_param.getCurve(); - // Bouncy Castle is not consistent - // about the letter it uses for the prime. if (x.compareTo(prime) >= 0) { // Cannot have point co-ordinates larger than this as everything // takes place modulo Q. @@ -652,18 +622,22 @@ public static byte[] recoverPubBytesFromSignature(int recId, // y-coord as there are two possibilities. // So it's encoded in the recId. ECPoint R = decompressKey(x, (recId & 1) == 1); - // 1.4. If nR != point at infinity, then do another iteration of + // 1.4. If nR != point at infinity, then do another iteration of // Step 1 (callers responsibility). if (!R.multiply(n).isInfinity()) { return null; } - // recover Q from the formula: s*G + (s+r)*Q = R => Q = (s+r)^(-1) (R-s*G) - BigInteger srInv = sig.s.add(sig.r).modInverse(n); + // recover Q from the formula: s*G + (s+r)*Q = R => Q = (s+r)^(-1) (R-s*G) + BigInteger sAddR = sig.s.add(sig.r).mod(n); + if (sAddR.equals(BigInteger.ZERO)) { + return null; + } + BigInteger srInv = sAddR.modInverse(n); BigInteger sNeg = BigInteger.ZERO.subtract(sig.s).mod(n); BigInteger coeff = srInv.multiply(sNeg).mod(n); - ECPoint.Fp q = (ECPoint.Fp) ECAlgorithms.sumOfTwoMultiplies(ecc_param + ECPoint.Fp q = (ECPoint.Fp) ECAlgorithms.sumOfTwoMultiplies(eccParam .getG(), coeff, R, srInv); return q.getEncoded(/* compressed */ false); } @@ -671,17 +645,17 @@ public static byte[] recoverPubBytesFromSignature(int recId, /** * Decompress a compressed public key (x co-ord and low-bit of y-coord). * - * @param xBN - + * @param xBN - * @param yBit - * @return - */ private static ECPoint decompressKey(BigInteger xBN, boolean yBit) { X9IntegerConverter x9 = new X9IntegerConverter(); - byte[] compEnc = x9.integerToBytes(xBN, 1 + x9.getByteLength(ecc_param + byte[] compEnc = x9.integerToBytes(xBN, 1 + x9.getByteLength(eccParam .getCurve())); compEnc[0] = (byte) (yBit ? 0x03 : 0x02); - return ecc_param.getCurve().decodePoint(compEnc); + return eccParam.getCurve().decodePoint(compEnc); } private static void check(boolean test, String message) { @@ -691,20 +665,29 @@ private static void check(boolean test, String message) { } /** - *
Verifies the given SM2 signature against the message bytes using the public key bytes.
- *
When using native SM2 verification, data must be 32 bytes, and no element may be - * larger than 520 bytes.
+ *+ * Verifies the given SM2 signature against the message bytes using the public + * key bytes. + *
+ *+ *
+ * When using native SM2 verification, data must be 32 bytes, and no element may + * be larger than 520 bytes. + *
* - * @param data Hash of the data to verify. + * @param data Hash of the data to verify. * @param signature signature. - * @param pub The public key bytes to use. + * @param pub The public key bytes to use. * @return - */ public static boolean verify(byte[] data, SM2Signature signature, byte[] pub) { + if (ByteArray.isEmpty(data) || signature == null || ByteArray.isEmpty(pub)) { + throw new IllegalArgumentException("Data, signature, or public key cannot be null"); + } SM2Signer signer = new SM2Signer(); - ECPublicKeyParameters params = new ECPublicKeyParameters(ecc_param - .getCurve().decodePoint(pub), ecc_param); + ECPublicKeyParameters params = new ECPublicKeyParameters(eccParam + .getCurve().decodePoint(pub), eccParam); signer.init(false, params); try { return signer.verifyHashSignature(data, signature.r, signature.s); @@ -719,60 +702,65 @@ public static boolean verify(byte[] data, SM2Signature signature, } /** - * Verifies the given ASN.1 encoded SM2 signature against a hash using the public key. + * Verifies a DER-encoded SM2 signature against the provided hash using the + * public key bytes. * - * @param data Hash of the data to verify. - * @param signature signature. - * @param pub The public key bytes to use. - * @return - + * @param data hash of the data to verify + * @param signature DER-encoded signature + * @param pub public key bytes to use + * @return false when the signature is malformed or invalid */ public static boolean verify(byte[] data, byte[] signature, byte[] pub) { - return verify(data, SM2Signature.decodeFromDER(signature), pub); + if (data == null || signature == null || pub == null) { + return false; + } + try { + return verify(data, SM2Signature.decodeFromDER(signature), pub); + } catch (IllegalArgumentException | SignatureException e) { + return false; + } } /** - *Verifies the given SM2 signature against the message bytes using the public key bytes.
+ * Verifies a DER-encoded SM2 signature against the message bytes using the
+ * public key bytes.
*
- * @param msg the message data to verify.
- * @param signature signature.
- * @param pub The public key bytes to use.
- * @return -
+ * @param message message bytes to verify
+ * @param signature DER-encoded signature
+ * @param pub public key bytes to use
+ * @return false when the signature is malformed or invalid
*/
- public static boolean verifyMessage(byte[] msg, SM2Signature signature,
- byte[] pub, @Nullable String userID) {
- SM2Signer signer = new SM2Signer();
- ECPublicKeyParameters params = new ECPublicKeyParameters(ecc_param
- .getCurve().decodePoint(pub), ecc_param);
- signer.init(false, params);
- try {
- return signer.verifySignature(msg, signature.r, signature.s, userID);
- } catch (NullPointerException npe) {
- // Bouncy Castle contains a bug that can cause NPEs given
- // specially crafted signatures.
- // Those signatures are inherently invalid/attack sigs so we just
- // fail them here rather than crash the thread.
- logger.error("Caught NPE inside bouncy castle", npe);
- return false;
- }
+ public static boolean verifyMessage(byte[] message, byte[] signature, byte[] pub) {
+ return verifyMessage(message, signature, pub, null);
}
/**
- * Verifies the given ASN.1 encoded SM2 signature against a hash using the public key.
+ * Verifies a DER-encoded SM2 signature against the message bytes using the
+ * public key bytes.
*
- * @param msg the message data to verify.
- * @param signature signature.
- * @param pub The public key bytes to use.
- * @return -
+ * @param message message bytes to verify
+ * @param signature DER-encoded signature
+ * @param pub public key bytes to use
+ * @param userID optional user identifier
+ * @return false when the signature is malformed or invalid
*/
- public static boolean verifyMessage(byte[] msg, byte[] signature, byte[] pub,
+ public static boolean verifyMessage(byte[] message, byte[] signature, byte[] pub,
@Nullable String userID) {
- return verifyMessage(msg, SM2Signature.decodeFromDER(signature), pub, userID);
+ if (message == null || signature == null || pub == null) {
+ return false;
+ }
+ try {
+ SM2 key = SM2.fromPublicOnly(pub);
+ byte[] messageHash = key.getSM2SignerForHash().generateSM3Hash(message);
+ return verify(messageHash, SM2Signature.decodeFromDER(signature), pub);
+ } catch (IllegalArgumentException | SignatureException e) {
+ return false;
+ }
}
-
/**
- * Returns true if the given pubkey is canonical, i.e. the correct length taking into account
- * compression.
+ * Returns true if the given pubkey is canonical, i.e. the correct length taking
+ * into account compression.
*
* @param pubkey -
* @return -
@@ -790,8 +778,8 @@ public static boolean isPubKeyCanonical(byte[] pubkey) {
}
/**
- * @param recId Which possible key to recover.
- * @param sig the R and S components of the signature, wrapped.
+ * @param recId Which possible key to recover.
+ * @param sig the R and S components of the signature, wrapped.
* @param messageHash Hash of the data that was signed.
* @return 20-byte address
*/
@@ -809,8 +797,8 @@ public static byte[] recoverAddressFromSignature(int recId,
}
/**
- * @param recId Which possible key to recover.
- * @param sig the R and S components of the signature, wrapped.
+ * @param recId Which possible key to recover.
+ * @param sig the R and S components of the signature, wrapped.
* @param messageHash Hash of the data that was signed.
* @return ECKey
*/
@@ -827,8 +815,9 @@ public static SM2 recoverFromSignature(int recId, SM2Signature sig,
}
/**
- * Returns true if this key doesn't have access to private key bytes. This may be because it was
- * never given any private key bytes to begin with (a watching key).
+ * Returns true if this key doesn't have access to private key bytes. This may
+ * be because it was never given any private key bytes to begin with (a watching
+ * key).
*
* @return -
*/
@@ -837,7 +826,8 @@ public boolean isPubKeyOnly() {
}
/**
- * Returns true if this key has access to private key bytes. Does the opposite of {@link
+ * Returns true if this key has access to private key bytes. Does the opposite
+ * of {@link
* #isPubKeyOnly()}.
*
* @return -
@@ -846,20 +836,20 @@ public boolean hasPrivKey() {
return privKey != null;
}
-
/**
- * Generates the NodeID based on this key, that is the public key without first format byte
+ * Generates the NodeID based on this key, that is the public key without first
+ * format byte
*/
public byte[] getNodeId() {
if (nodeId == null) {
nodeId = pubBytesWithoutFormat(this.pub);
}
- return nodeId;
+ return Arrays.copyOf(nodeId, nodeId.length);
}
-
/**
- * Gets the public key in the form of an elliptic curve point object from Bouncy Castle.
+ * Gets the public key in the form of an elliptic curve point object from Bouncy
+ * Castle.
*
* @return -
*/
@@ -868,8 +858,9 @@ public ECPoint getPubKeyPoint() {
}
/**
- * Gets the private key in the form of an integer field element. The public key is derived by
- * performing EC point addition this number of times (i.e. point multiplying).
+ * Gets the private key in the form of an integer field element. The public key
+ * is derived by performing EC point addition this number of times (i.e. point
+ * multiplying).
*
* @return -
* @throws IllegalStateException if the private key bytes are not available.
@@ -885,42 +876,13 @@ public BigInteger getPrivKey() {
}
public String toString() {
- StringBuilder b = new StringBuilder();
- b.append("pub:").append(Hex.toHexString(pub.getEncoded(false)));
- return b.toString();
- }
-
- /**
- * Produce a string rendering of the ECKey INCLUDING the private key. Unless you absolutely need
- * the private key it is better for security reasons to just use toString().
- *
- * @return -
- */
- public String toStringWithPrivate() {
- StringBuilder b = new StringBuilder();
- b.append(toString());
- if (privKey != null && privKey instanceof BCECPrivateKey) {
- b.append(" priv:").append(Hex.toHexString(((BCECPrivateKey)
- privKey).getD().toByteArray()));
- }
- return b.toString();
- }
-
- /**
- * Verifies the given ASN.1 encoded SM2 signature against a hash using the public key.
- *
- * @param data Hash of the data to verify.
- * @param signature signature.
- * @return -
- */
- public boolean verify(byte[] data, byte[] signature) {
- return SM2.verify(data, signature, getPubKey());
+ return "pub:" + Hex.toHexString(pub.getEncoded(false));
}
/**
* Verifies the given R/S pair (signature) against a hash using the public key.
*
- * @param sigHash -
+ * @param sigHash -
* @param signature -
* @return -
*/
@@ -929,8 +891,8 @@ public boolean verify(byte[] sigHash, SM2Signature signature) {
}
/**
- * Returns true if this pubkey is canonical, i.e. the correct length taking into account
- * compression.
+ * Returns true if this pubkey is canonical, i.e. the correct length taking into
+ * account compression.
*
* @return -
*/
@@ -939,8 +901,8 @@ public boolean isPubKeyCanonical() {
}
/**
- * Returns a 32 byte array containing the private key, or null if the key is encrypted or public
- * only
+ * Returns a 32 byte array containing the private key, or null if the key is
+ * encrypted or public only
*
* @return -
*/
@@ -967,10 +929,10 @@ public boolean equals(Object o) {
SM2 ecKey = (SM2) o;
- if (privKey != null && !privKey.equals(ecKey.privKey)) {
+ if (!Objects.equals(privKey, ecKey.privKey)) {
return false;
}
- return pub == null || pub.equals(ecKey.pub);
+ return Objects.equals(pub, ecKey.pub);
}
@Override
@@ -978,7 +940,6 @@ public int hashCode() {
return Arrays.hashCode(getPubKey());
}
-
public static class SM2Signature implements SignatureInterface {
/**
@@ -988,8 +949,8 @@ public static class SM2Signature implements SignatureInterface {
public byte v;
/**
- * Constructs a signature with the given components. Does NOT automatically canonicalise the
- * signature.
+ * Constructs a signature with the given components. Does NOT automatically
+ * canonicalise the signature.
*
* @param r -
* @param s -
@@ -1005,11 +966,6 @@ public SM2Signature(byte[] r, byte[] s, byte v) {
this.v = v;
}
- /**
- * t
- *
- * @return -
- */
private static SM2Signature fromComponents(byte[] r, byte[] s) {
return new SM2Signature(new BigInteger(1, r), new BigInteger(1,
s));
@@ -1021,13 +977,36 @@ private static SM2Signature fromComponents(byte[] r, byte[] s) {
* @param v -
* @return -
*/
- public static SM2Signature fromComponents(byte[] r, byte[] s, byte
- v) {
+ public static SM2Signature fromComponents(byte[] r, byte[] s, byte v) {
SM2Signature signature = fromComponents(r, s);
signature.v = v;
return signature;
}
+ public static SM2Signature decodeFromDER(byte[] signature) throws SignatureException {
+ if (ByteArray.isEmpty(signature) || signature.length > 128) {
+ throw new SignatureException("Invalid DER signature length");
+ }
+
+ try (ASN1InputStream inputStream = new ASN1InputStream(signature)) {
+ ASN1Primitive primitive = inputStream.readObject();
+ if (!(primitive instanceof ASN1Sequence)) {
+ throw new SignatureException("Invalid DER signature format");
+ }
+
+ ASN1Sequence sequence = (ASN1Sequence) primitive;
+ if (sequence.size() != 2) {
+ throw new SignatureException("Invalid DER signature component count");
+ }
+
+ BigInteger r = ASN1Integer.getInstance(sequence.getObjectAt(0)).getPositiveValue();
+ BigInteger s = ASN1Integer.getInstance(sequence.getObjectAt(1)).getPositiveValue();
+ return new SM2Signature(r, s);
+ } catch (IOException | IllegalArgumentException e) {
+ throw new SignatureException("Could not decode DER signature", e);
+ }
+ }
+
public static boolean validateComponents(BigInteger r, BigInteger s,
byte v) {
@@ -1048,59 +1027,22 @@ public static boolean validateComponents(BigInteger r, BigInteger s,
return isLessThan(s, SM2.SM2_N);
}
- public static SM2Signature decodeFromDER(byte[] bytes) {
- ASN1InputStream decoder = null;
- try {
- decoder = new ASN1InputStream(bytes);
- DLSequence seq = (DLSequence) decoder.readObject();
- if (seq == null) {
- throw new RuntimeException("Reached past end of ASN.1 "
- + "stream.");
- }
- ASN1Integer r, s;
- try {
- r = (ASN1Integer) seq.getObjectAt(0);
- s = (ASN1Integer) seq.getObjectAt(1);
- } catch (ClassCastException e) {
- throw new IllegalArgumentException(e);
- }
- // OpenSSL deviates from the DER spec by interpreting these
- // values as unsigned, though they should not be
- // Thus, we always use the positive versions. See:
- // http://r6.ca/blog/20111119T211504Z.html
- return new SM2Signature(r.getPositiveValue(), s
- .getPositiveValue());
- } catch (IOException e) {
- throw new RuntimeException(e);
- } finally {
- if (decoder != null) {
- try {
- decoder.close();
- } catch (IOException x) {
-
- }
- }
- }
- }
-
public boolean validateComponents() {
return validateComponents(r, s, v);
}
-
/**
* @return -
*/
public String toBase64() {
- byte[] sigData = new byte[65]; // 1 header + 32 bytes for R + 32
+ byte[] sigData = new byte[65]; // 1 header + 32 bytes for R + 32
// bytes for S
sigData[0] = v;
System.arraycopy(bigIntegerToBytes(this.r, 32), 0, sigData, 1, 32);
System.arraycopy(bigIntegerToBytes(this.s, 32), 0, sigData, 33, 32);
- return new String(Base64.encode(sigData), Charset.forName("UTF-8"));
+ return new String(Base64.encode(sigData), StandardCharsets.UTF_8);
}
-
public byte[] toByteArray() {
final byte fixedV = this.v >= 27
? (byte) (this.v - 27)
@@ -1109,7 +1051,7 @@ public byte[] toByteArray() {
return ByteUtil.merge(
ByteUtil.bigIntegerToBytes(this.r, 32),
ByteUtil.bigIntegerToBytes(this.s, 32),
- new byte[]{fixedV});
+ new byte[] { fixedV });
}
public String toHex() {
@@ -1140,5 +1082,4 @@ public int hashCode() {
return result;
}
}
-
}
diff --git a/crypto/src/main/java/org/tron/common/crypto/sm2/SM2Signer.java b/crypto/src/main/java/org/tron/common/crypto/sm2/SM2Signer.java
index 817b909de58..a0170b9c2d6 100644
--- a/crypto/src/main/java/org/tron/common/crypto/sm2/SM2Signer.java
+++ b/crypto/src/main/java/org/tron/common/crypto/sm2/SM2Signer.java
@@ -11,20 +11,20 @@
import org.bouncycastle.crypto.params.ECPrivateKeyParameters;
import org.bouncycastle.crypto.params.ECPublicKeyParameters;
import org.bouncycastle.crypto.params.ParametersWithID;
-import org.bouncycastle.crypto.params.ParametersWithRandom;
import org.bouncycastle.crypto.signers.DSAKCalculator;
-import org.bouncycastle.crypto.signers.RandomDSAKCalculator;
+import org.bouncycastle.crypto.signers.HMacDSAKCalculator;
import org.bouncycastle.math.ec.ECConstants;
import org.bouncycastle.math.ec.ECFieldElement;
import org.bouncycastle.math.ec.ECMultiplier;
import org.bouncycastle.math.ec.ECPoint;
import org.bouncycastle.math.ec.FixedPointCombMultiplier;
import org.bouncycastle.util.BigIntegers;
+import org.tron.common.utils.ByteArray;
public class SM2Signer
implements ECConstants {
- private final DSAKCalculator kCalculator = new RandomDSAKCalculator();
+ private final DSAKCalculator kCalculator = new HMacDSAKCalculator(new SM3Digest());
private byte[] userID;
@@ -33,9 +33,10 @@ public class SM2Signer
private ECPoint pubPoint;
private ECKeyParameters ecKey;
- private SecureRandom random;
-
public void init(boolean forSigning, CipherParameters param) {
+ if (param == null) {
+ throw new IllegalArgumentException("CipherParameters cannot be null");
+ }
CipherParameters baseParam;
if (param instanceof ParametersWithID) {
@@ -46,22 +47,12 @@ public void init(boolean forSigning, CipherParameters param) {
userID = new byte[0];
}
+ ecKey = (ECKeyParameters) baseParam;
+ ecParams = ecKey.getParameters();
+
if (forSigning) {
- if (baseParam instanceof ParametersWithRandom) {
- ParametersWithRandom rParam = (ParametersWithRandom) baseParam;
-
- ecKey = (ECKeyParameters) rParam.getParameters();
- ecParams = ecKey.getParameters();
- kCalculator.init(ecParams.getN(), rParam.getRandom());
- } else {
- ecKey = (ECKeyParameters) baseParam;
- ecParams = ecKey.getParameters();
- kCalculator.init(ecParams.getN(), new SecureRandom());
- }
pubPoint = ecParams.getG().multiply(((ECPrivateKeyParameters) ecKey).getD()).normalize();
} else {
- ecKey = (ECKeyParameters) baseParam;
- ecParams = ecKey.getParameters();
pubPoint = ((ECPublicKeyParameters) ecKey).getQ();
}
@@ -84,8 +75,9 @@ public BigInteger[] generateSignature(byte[] message) {
*/
public byte[] generateSM3Hash(byte[] message) {
- //byte[] msg = message.getBytes();
-
+ if (message == null) {
+ throw new IllegalArgumentException("Message cannot be null");
+ }
SM3Digest digest = new SM3Digest();
byte[] z = getZ(digest);
@@ -102,9 +94,9 @@ public byte[] generateSM3Hash(byte[] message) {
* generate the signature from the 32 byte hash
*/
public BigInteger[] generateHashSignature(byte[] hash) {
- if (hash.length != 32) {
+ if (ByteArray.isEmpty(hash) || hash.length != 32) {
throw new IllegalArgumentException("Expected 32 byte input to " +
- "ECDSA signature, not " + hash.length);
+ "SM2 signature, not " + (hash == null ? "null" : hash.length));
}
BigInteger n = ecParams.getN();
BigInteger e = calculateE(hash);
@@ -114,6 +106,9 @@ public BigInteger[] generateHashSignature(byte[] hash) {
ECMultiplier basePointMultiplier = createBasePointMultiplier();
+ // Initialize the deterministic K calculator with the private key and message hash
+ kCalculator.init(n, d, hash);
+
// 5.2.1 Draft RFC: SM2 Public Key Algorithms
do // generate s
{
@@ -147,6 +142,9 @@ public BigInteger[] generateHashSignature(byte[] hash) {
*/
public boolean verifySignature(byte[] message, BigInteger r, BigInteger s,
@Nullable String userID) {
+ if (message == null || r == null || s == null) {
+ throw new IllegalArgumentException("Message, R, or S cannot be null");
+ }
BigInteger n = ecParams.getN();
// 5.3.1 Draft RFC: SM2 Public Key Algorithms
@@ -188,6 +186,12 @@ public boolean verifySignature(byte[] message, BigInteger r, BigInteger s,
* verify the hash signature
*/
public boolean verifyHashSignature(byte[] hash, BigInteger r, BigInteger s) {
+ if (ByteArray.isEmpty(hash)) {
+ throw new IllegalArgumentException("Hash cannot be empty");
+ }
+ if (r == null || s == null) {
+ throw new IllegalArgumentException("R or S cannot be null");
+ }
BigInteger n = ecParams.getN();
// 5.3.1 Draft RFC: SM2 Public Key Algorithms
@@ -255,8 +259,10 @@ protected ECMultiplier createBasePointMultiplier() {
}
protected BigInteger calculateE(byte[] message) {
+ if (message == null) {
+ throw new IllegalArgumentException("Message cannot be null");
+ }
return new BigInteger(1, message);
}
}
-
diff --git a/framework/src/main/java/org/tron/core/Wallet.java b/framework/src/main/java/org/tron/core/Wallet.java
index 39e8f06c281..665ffb5f77a 100755
--- a/framework/src/main/java/org/tron/core/Wallet.java
+++ b/framework/src/main/java/org/tron/core/Wallet.java
@@ -780,7 +780,7 @@ public WitnessList getPaginatedNowWitnessList(long offset, long limit) throws
if (limit > WITNESS_COUNT_LIMIT_MAX) {
limit = WITNESS_COUNT_LIMIT_MAX;
}
-
+
/*
In the maintenance period, the VoteStores will be cleared.
To avoid the race condition of VoteStores deleted but Witness vote counts not updated,
@@ -1502,8 +1502,8 @@ public Protocol.ChainParameters getChainParameters() {
builder.addChainParameter(Protocol.ChainParameters.ChainParameter.newBuilder()
.setKey("getAllowTvmSelfdestructRestriction")
.setValue(dbManager.getDynamicPropertiesStore().getAllowTvmSelfdestructRestriction())
- .build());
-
+ .build());
+
builder.addChainParameter(Protocol.ChainParameters.ChainParameter.newBuilder()
.setKey("getProposalExpireTime")
.setValue(dbManager.getDynamicPropertiesStore().getProposalExpireTime())
@@ -2320,57 +2320,57 @@ public TransactionCapsule createShieldedTransaction(PrivateParameters request)
checkCmValid(shieldedSpends, shieldedReceives);
- // add
- if (!ArrayUtils.isEmpty(transparentFromAddress)) {
- builder.setTransparentInput(transparentFromAddress, fromAmount);
- }
+ try {
+ // add
+ if (!ArrayUtils.isEmpty(transparentFromAddress)) {
+ builder.setTransparentInput(transparentFromAddress, fromAmount);
+ }
- if (!ArrayUtils.isEmpty(transparentToAddress)) {
- builder.setTransparentOutput(transparentToAddress, toAmount);
- }
-
- // from shielded to public, without shielded receive, will create a random shielded address
- if (!shieldedSpends.isEmpty()
- && !ArrayUtils.isEmpty(transparentToAddress)
- && shieldedReceives.isEmpty()) {
- shieldedReceives = new ArrayList<>();
- ReceiveNote receiveNote = createReceiveNoteRandom(0);
- shieldedReceives.add(receiveNote);
- }
-
- // input
- if (!(ArrayUtils.isEmpty(ask) || ArrayUtils.isEmpty(nsk) || ArrayUtils.isEmpty(ovk))) {
- ExpandedSpendingKey expsk = new ExpandedSpendingKey(ask, nsk, ovk);
- for (SpendNote spendNote : shieldedSpends) {
- GrpcAPI.Note note = spendNote.getNote();
- PaymentAddress paymentAddress = KeyIo.decodePaymentAddress(note.getPaymentAddress());
- if (paymentAddress == null) {
- throw new ZksnarkException(PAYMENT_ADDRESS_FORMAT_WRONG);
+ if (!ArrayUtils.isEmpty(transparentToAddress)) {
+ builder.setTransparentOutput(transparentToAddress, toAmount);
+ }
+
+ // from shielded to public, without shielded receive, will create a random shielded address
+ if (!shieldedSpends.isEmpty()
+ && !ArrayUtils.isEmpty(transparentToAddress)
+ && shieldedReceives.isEmpty()) {
+ shieldedReceives = new ArrayList<>();
+ ReceiveNote receiveNote = createReceiveNoteRandom(0);
+ shieldedReceives.add(receiveNote);
+ }
+
+ // input
+ if (!(ArrayUtils.isEmpty(ask) || ArrayUtils.isEmpty(nsk) || ArrayUtils.isEmpty(ovk))) {
+ ExpandedSpendingKey expsk = new ExpandedSpendingKey(ask, nsk, ovk);
+ for (SpendNote spendNote : shieldedSpends) {
+ GrpcAPI.Note note = spendNote.getNote();
+ PaymentAddress paymentAddress = KeyIo.decodePaymentAddress(note.getPaymentAddress());
+ if (paymentAddress == null) {
+ throw new ZksnarkException(PAYMENT_ADDRESS_FORMAT_WRONG);
+ }
+ Note baseNote = new Note(paymentAddress.getD(),
+ paymentAddress.getPkD(), note.getValue(), note.getRcm().toByteArray());
+
+ IncrementalMerkleVoucherContainer voucherContainer = new IncrementalMerkleVoucherCapsule(
+ spendNote.getVoucher()).toMerkleVoucherContainer();
+ builder.addSpend(expsk,
+ baseNote,
+ spendNote.getAlpha().toByteArray(),
+ spendNote.getVoucher().getRt().toByteArray(),
+ voucherContainer);
}
- Note baseNote = new Note(paymentAddress.getD(),
- paymentAddress.getPkD(), note.getValue(), note.getRcm().toByteArray());
-
- IncrementalMerkleVoucherContainer voucherContainer = new IncrementalMerkleVoucherCapsule(
- spendNote.getVoucher()).toMerkleVoucherContainer();
- builder.addSpend(expsk,
- baseNote,
- spendNote.getAlpha().toByteArray(),
- spendNote.getVoucher().getRt().toByteArray(),
- voucherContainer);
}
- }
- // output
- shieldedOutput(shieldedReceives, builder, ovk);
+ // output
+ shieldedOutput(shieldedReceives, builder, ovk);
- TransactionCapsule transactionCapsule = null;
- try {
- transactionCapsule = builder.build();
+ return builder.build();
+ } catch (ArithmeticException e) {
+ throw new ZksnarkException("shielded amount overflow");
} catch (ZksnarkException e) {
logger.error("createShieldedTransaction except, error is " + e.toString());
throw new ZksnarkException(e.toString());
}
- return transactionCapsule;
}
@@ -2422,58 +2422,58 @@ public TransactionCapsule createShieldedTransactionWithoutSpendAuthSig(
checkCmValid(shieldedSpends, shieldedReceives);
- // add
- if (!ArrayUtils.isEmpty(transparentFromAddress)) {
- builder.setTransparentInput(transparentFromAddress, fromAmount);
- }
+ try {
+ // add
+ if (!ArrayUtils.isEmpty(transparentFromAddress)) {
+ builder.setTransparentInput(transparentFromAddress, fromAmount);
+ }
- if (!ArrayUtils.isEmpty(transparentToAddress)) {
- builder.setTransparentOutput(transparentToAddress, toAmount);
- }
+ if (!ArrayUtils.isEmpty(transparentToAddress)) {
+ builder.setTransparentOutput(transparentToAddress, toAmount);
+ }
- // from shielded to public, without shielded receive, will create a random shielded address
- if (!shieldedSpends.isEmpty()
- && !ArrayUtils.isEmpty(transparentToAddress)
- && shieldedReceives.isEmpty()) {
- shieldedReceives = new ArrayList<>();
- ReceiveNote receiveNote = createReceiveNoteRandom(0);
- shieldedReceives.add(receiveNote);
- }
+ // from shielded to public, without shielded receive, will create a random shielded address
+ if (!shieldedSpends.isEmpty()
+ && !ArrayUtils.isEmpty(transparentToAddress)
+ && shieldedReceives.isEmpty()) {
+ shieldedReceives = new ArrayList<>();
+ ReceiveNote receiveNote = createReceiveNoteRandom(0);
+ shieldedReceives.add(receiveNote);
+ }
- // input
- if (!(ArrayUtils.isEmpty(ak) || ArrayUtils.isEmpty(nsk) || ArrayUtils.isEmpty(ovk))) {
- for (SpendNote spendNote : shieldedSpends) {
- GrpcAPI.Note note = spendNote.getNote();
- PaymentAddress paymentAddress = KeyIo.decodePaymentAddress(note.getPaymentAddress());
- if (paymentAddress == null) {
- throw new ZksnarkException(PAYMENT_ADDRESS_FORMAT_WRONG);
+ // input
+ if (!(ArrayUtils.isEmpty(ak) || ArrayUtils.isEmpty(nsk) || ArrayUtils.isEmpty(ovk))) {
+ for (SpendNote spendNote : shieldedSpends) {
+ GrpcAPI.Note note = spendNote.getNote();
+ PaymentAddress paymentAddress = KeyIo.decodePaymentAddress(note.getPaymentAddress());
+ if (paymentAddress == null) {
+ throw new ZksnarkException(PAYMENT_ADDRESS_FORMAT_WRONG);
+ }
+ Note baseNote = new Note(paymentAddress.getD(),
+ paymentAddress.getPkD(), note.getValue(), note.getRcm().toByteArray());
+
+ IncrementalMerkleVoucherContainer voucherContainer = new IncrementalMerkleVoucherCapsule(
+ spendNote.getVoucher()).toMerkleVoucherContainer();
+ builder.addSpend(ak,
+ nsk,
+ ovk,
+ baseNote,
+ spendNote.getAlpha().toByteArray(),
+ spendNote.getVoucher().getRt().toByteArray(),
+ voucherContainer);
}
- Note baseNote = new Note(paymentAddress.getD(),
- paymentAddress.getPkD(), note.getValue(), note.getRcm().toByteArray());
-
- IncrementalMerkleVoucherContainer voucherContainer = new IncrementalMerkleVoucherCapsule(
- spendNote.getVoucher()).toMerkleVoucherContainer();
- builder.addSpend(ak,
- nsk,
- ovk,
- baseNote,
- spendNote.getAlpha().toByteArray(),
- spendNote.getVoucher().getRt().toByteArray(),
- voucherContainer);
}
- }
- // output
- shieldedOutput(shieldedReceives, builder, ovk);
+ // output
+ shieldedOutput(shieldedReceives, builder, ovk);
- TransactionCapsule transactionCapsule = null;
- try {
- transactionCapsule = builder.buildWithoutAsk();
+ return builder.buildWithoutAsk();
+ } catch (ArithmeticException e) {
+ throw new ZksnarkException("shielded amount overflow");
} catch (ZksnarkException e) {
logger.error("createShieldedTransaction exception, error is " + e.toString());
throw new ZksnarkException(e.toString());
}
- return transactionCapsule;
}
@@ -2492,7 +2492,6 @@ private void shieldedOutput(List