Skip to content

Commit a94a3ec

Browse files
committed
refactor(crypto): simplify SM2 to blockchain-adapted hash-signing model
1 parent 6211729 commit a94a3ec

4 files changed

Lines changed: 29 additions & 199 deletions

File tree

crypto/src/main/java/org/tron/common/crypto/sm2/SM2.java

Lines changed: 16 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,16 @@
4545
import org.tron.common.utils.ByteUtil;
4646

4747
/**
48-
* Implement Chinese Commercial Cryptographic Standard of SM2
48+
* Blockchain-adapted SM2 signature implementation (GB/T 32918 / GM/T 0003).
49+
*
50+
* <p><b>Non-standard usage:</b> The SM2 standard computes signatures over
51+
* {@code e = SM3(Z_A || M)}, where {@code Z_A} binds the signer's identity and public key to the
52+
* message. This implementation omits the {@code Z_A} step and signs the 32-byte transaction hash
53+
* directly, consistent with how ECDSA is used on TRON. This keeps both cryptographic engines
54+
* interchangeable at the {@link org.tron.common.crypto.SignInterface} level.
55+
*
56+
* <p><b>Note:</b> Signatures produced here are not interoperable with standard SM2
57+
* implementations that apply the {@code Z_A} pre-hash.
4958
*/
5059
@Slf4j(topic = "crypto")
5160
public class SM2 implements Serializable, SignInterface {
@@ -385,11 +394,6 @@ public static byte[] signatureToKeyBytes(byte[] messageHash,
385394
return key;
386395
}
387396

388-
public byte[] hash(byte[] message) {
389-
SM2Signer signer = this.getSM2SignerForHash();
390-
return signer.generateSM3Hash(message);
391-
}
392-
393397
@Override
394398
public byte[] getPrivateKey() {
395399
return getPrivKeyBytes();
@@ -507,40 +511,6 @@ public byte[] Base64toBytes(String signature) {
507511
return ByteUtil.appendByte(temp, first);
508512
}
509513

510-
/**
511-
* Takes the message of data and returns the SM2 signature
512-
*
513-
* @param message -
514-
* @return -
515-
* @throws IllegalStateException if this ECKey does not have the private part.
516-
*/
517-
public SM2Signature signMessage(byte[] message, @Nullable String userID) {
518-
SM2Signature sig = signMsg(message, userID);
519-
520-
SM2Signer signer = getSigner();
521-
byte[] messageHash = signer.generateSM3Hash(message);
522-
sig.v = (byte) (findRecId(sig, messageHash) + 27);
523-
return sig;
524-
}
525-
526-
/**
527-
* Signs the given hash and returns the R and S components as BigIntegers and
528-
* putData them in SM2Signature
529-
*
530-
* @param msg to sign
531-
* @return SM2Signature signature that contains the R and S components
532-
*/
533-
public SM2Signature signMsg(byte[] msg, @Nullable String userID) {
534-
if (msg == null) {
535-
throw new IllegalArgumentException("Expected signature message of " +
536-
"SM2 is null");
537-
}
538-
// No decryption of private key required.
539-
SM2Signer signer = getSigner();
540-
BigInteger[] componets = signer.generateSignature(msg);
541-
return new SM2Signature(componets[0], componets[1]);
542-
}
543-
544514
private int findRecId(SM2Signature sig, byte[] messageHash) {
545515
byte[] thisKey = this.pub.getEncoded(/* compressed */ false);
546516
for (int i = 0; i < 4; i++) {
@@ -557,17 +527,18 @@ private SM2Signer getSigner() {
557527
SM2Signer signer = new SM2Signer();
558528
BigInteger d = getPrivKey();
559529
ECPrivateKeyParameters privateKeyParameters = new ECPrivateKeyParameters(d, eccParam);
560-
signer.init(true, privateKeyParameters);
530+
signer.init(privateKeyParameters);
561531
return signer;
562532
}
563533

564534
/**
565-
* used to generate the SM3 hash for SM2 signature generation or verification
535+
* Returns an {@link SM2Signer} initialized with this key's public key, ready to call
536+
* {@link SM2Signer#verifyHashSignature}.
566537
*/
567-
public SM2Signer getSM2SignerForHash() {
538+
public SM2Signer getVerifier() {
568539
SM2Signer signer = new SM2Signer();
569540
ECPublicKeyParameters publicKeyParameters = new ECPublicKeyParameters(pub, eccParam);
570-
signer.init(false, publicKeyParameters);
541+
signer.init(publicKeyParameters);
571542
return signer;
572543
}
573544

@@ -686,7 +657,7 @@ public static boolean verify(byte[] data, SM2Signature signature,
686657
SM2Signer signer = new SM2Signer();
687658
ECPublicKeyParameters params = new ECPublicKeyParameters(eccParam
688659
.getCurve().decodePoint(pub), eccParam);
689-
signer.init(false, params);
660+
signer.init(params);
690661
try {
691662
return signer.verifyHashSignature(data, signature.r, signature.s);
692663
} catch (NullPointerException npe) {

crypto/src/main/java/org/tron/common/crypto/sm2/SM2Signer.java

Lines changed: 10 additions & 135 deletions
Original file line numberDiff line numberDiff line change
@@ -1,92 +1,42 @@
11
package org.tron.common.crypto.sm2;
22

33
import java.math.BigInteger;
4-
import javax.annotation.Nullable;
54
import org.bouncycastle.crypto.CipherParameters;
6-
import org.bouncycastle.crypto.Digest;
75
import org.bouncycastle.crypto.digests.SM3Digest;
86
import org.bouncycastle.crypto.params.ECDomainParameters;
97
import org.bouncycastle.crypto.params.ECKeyParameters;
108
import org.bouncycastle.crypto.params.ECPrivateKeyParameters;
119
import org.bouncycastle.crypto.params.ECPublicKeyParameters;
12-
import org.bouncycastle.crypto.params.ParametersWithID;
1310
import org.bouncycastle.crypto.signers.DSAKCalculator;
1411
import org.bouncycastle.crypto.signers.HMacDSAKCalculator;
1512
import org.bouncycastle.math.ec.ECConstants;
16-
import org.bouncycastle.math.ec.ECFieldElement;
1713
import org.bouncycastle.math.ec.ECMultiplier;
1814
import org.bouncycastle.math.ec.ECPoint;
1915
import org.bouncycastle.math.ec.FixedPointCombMultiplier;
20-
import org.bouncycastle.util.BigIntegers;
2116
import org.tron.common.utils.ByteArray;
2217

18+
/**
19+
* Low-level SM2 signer used by {@link org.tron.common.crypto.sm2.SM2}.
20+
*
21+
* <p>Exposes two operations: {@link #generateHashSignature} (sign a pre-computed 32-byte hash)
22+
* and {@link #verifyHashSignature} (verify against a pre-computed hash). The standard SM2
23+
* {@code Z_A} pre-hash step is intentionally absent; see {@link org.tron.common.crypto.sm2.SM2}
24+
* for the rationale.
25+
*/
2326
public class SM2Signer
2427
implements ECConstants {
2528

2629
private final DSAKCalculator kCalculator = new HMacDSAKCalculator(new SM3Digest());
2730

28-
private byte[] userID;
29-
30-
private int curveLength;
3131
private ECDomainParameters ecParams;
32-
private ECPoint pubPoint;
3332
private ECKeyParameters ecKey;
3433

35-
public void init(boolean forSigning, CipherParameters param) {
34+
public void init(CipherParameters param) {
3635
if (param == null) {
3736
throw new IllegalArgumentException("CipherParameters cannot be null");
3837
}
39-
CipherParameters baseParam;
40-
41-
if (param instanceof ParametersWithID) {
42-
baseParam = ((ParametersWithID) param).getParameters();
43-
userID = ((ParametersWithID) param).getID();
44-
} else {
45-
baseParam = param;
46-
userID = new byte[0];
47-
}
48-
49-
ecKey = (ECKeyParameters) baseParam;
38+
ecKey = (ECKeyParameters) param;
5039
ecParams = ecKey.getParameters();
51-
52-
if (forSigning) {
53-
pubPoint = ecParams.getG().multiply(((ECPrivateKeyParameters) ecKey).getD()).normalize();
54-
} else {
55-
pubPoint = ((ECPublicKeyParameters) ecKey).getQ();
56-
}
57-
58-
curveLength = (ecParams.getCurve().getFieldSize() + 7) / 8;
59-
}
60-
61-
62-
/**
63-
* generate the signature for the message
64-
*
65-
* @param message plaintext
66-
*/
67-
public BigInteger[] generateSignature(byte[] message) {
68-
byte[] eHash = generateSM3Hash(message);
69-
return generateHashSignature(eHash);
70-
}
71-
72-
/**
73-
* generate the signature for the message
74-
*/
75-
76-
public byte[] generateSM3Hash(byte[] message) {
77-
if (message == null) {
78-
throw new IllegalArgumentException("Message cannot be null");
79-
}
80-
SM3Digest digest = new SM3Digest();
81-
byte[] z = getZ(digest);
82-
83-
digest.update(z, 0, z.length);
84-
digest.update(message, 0, message.length);
85-
86-
byte[] eHash = new byte[digest.getDigestSize()];
87-
88-
digest.doFinal(eHash, 0);
89-
return eHash;
9040
}
9141

9242
/**
@@ -136,51 +86,6 @@ public BigInteger[] generateHashSignature(byte[] hash) {
13686
return new BigInteger[]{r, s};
13787
}
13888

139-
/**
140-
* verify the message signature
141-
*/
142-
public boolean verifySignature(byte[] message, BigInteger r, BigInteger s,
143-
@Nullable String userID) {
144-
if (message == null || r == null || s == null) {
145-
throw new IllegalArgumentException("Message, R, or S cannot be null");
146-
}
147-
BigInteger n = ecParams.getN();
148-
149-
// 5.3.1 Draft RFC: SM2 Public Key Algorithms
150-
// B1
151-
if (r.compareTo(ONE) < 0 || r.compareTo(n) >= 0) {
152-
return false;
153-
}
154-
155-
// B2
156-
if (s.compareTo(ONE) < 0 || s.compareTo(n) >= 0) {
157-
return false;
158-
}
159-
160-
ECPoint q = ((ECPublicKeyParameters) ecKey).getQ();
161-
162-
if (userID != null) {
163-
this.userID = userID.getBytes();
164-
}
165-
byte[] eHash = generateSM3Hash(message);
166-
167-
// B4
168-
BigInteger e = calculateE(eHash);
169-
170-
// B5
171-
BigInteger t = r.add(s).mod(n);
172-
if (t.equals(ZERO)) {
173-
return false;
174-
} else {
175-
// B6
176-
ECPoint x1y1 = ecParams.getG().multiply(s);
177-
x1y1 = x1y1.add(q.multiply(t)).normalize();
178-
179-
// B7
180-
return r.equals(e.add(x1y1.getAffineXCoord().toBigInteger()).mod(n));
181-
}
182-
}
183-
18489
/**
18590
* verify the hash signature
18691
*/
@@ -224,36 +129,6 @@ public boolean verifyHashSignature(byte[] hash, BigInteger r, BigInteger s) {
224129
}
225130
}
226131

227-
private byte[] getZ(Digest digest) {
228-
229-
//addUserID(digest, userID);
230-
231-
addFieldElement(digest, ecParams.getCurve().getA());
232-
addFieldElement(digest, ecParams.getCurve().getB());
233-
addFieldElement(digest, ecParams.getG().getAffineXCoord());
234-
addFieldElement(digest, ecParams.getG().getAffineYCoord());
235-
addFieldElement(digest, pubPoint.getAffineXCoord());
236-
addFieldElement(digest, pubPoint.getAffineYCoord());
237-
238-
byte[] rv = new byte[digest.getDigestSize()];
239-
240-
digest.doFinal(rv, 0);
241-
242-
return rv;
243-
}
244-
245-
private void addUserID(Digest digest, byte[] userID) {
246-
int len = userID.length * 8;
247-
digest.update((byte) (len >> 8 & 0xFF));
248-
digest.update((byte) (len & 0xFF));
249-
digest.update(userID, 0, userID.length);
250-
}
251-
252-
private void addFieldElement(Digest digest, ECFieldElement v) {
253-
byte[] p = BigIntegers.asUnsignedByteArray(curveLength, v.toBigInteger());
254-
digest.update(p, 0, p.length);
255-
}
256-
257132
protected ECMultiplier createBasePointMultiplier() {
258133
return new FixedPointCombMultiplier();
259134
}

framework/src/test/java/org/tron/common/crypto/SM2KeyTest.java

Lines changed: 3 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
import org.junit.Test;
2323
import org.tron.common.crypto.sm2.SM2;
2424
import org.tron.common.crypto.sm2.SM2Signer;
25+
import org.tron.common.utils.Sha256Hash;
2526
import org.tron.core.Wallet;
2627

2728
/**
@@ -117,17 +118,6 @@ public void testInvalidSignatureLength() throws SignatureException {
117118
fail("Expecting a SignatureException for invalid signature length");
118119
}
119120

120-
@Test
121-
public void testSM3Hash() {
122-
SM2 key = SM2.fromPublicOnly(pubKey);
123-
SM2Signer signer = key.getSM2SignerForHash();
124-
String message = "message digest";
125-
byte[] hash = signer.generateSM3Hash(message.getBytes());
126-
assertEquals("2A723761EAE35429DF643648FD69FB7787E7FC32F321BFAF7E294390F529BAF4",
127-
Hex.toHexString(hash).toUpperCase());
128-
}
129-
130-
131121
@Test
132122
public void testSignatureToKeyBytes() throws SignatureException {
133123
SM2 key = SM2.fromPrivate(privateKey);
@@ -329,12 +319,12 @@ public void testDifferentMessagesSignatures() {
329319
public void testSignatureVerification() {
330320
SM2 key = SM2.fromPrivate(privateKey);
331321
String message = "Hello, SM2 deterministic signature test!";
332-
byte[] hash = key.getSM2SignerForHash().generateSM3Hash(message.getBytes());
322+
byte[] hash = Sha256Hash.hash(false, message.getBytes());
333323

334324
SM2.SM2Signature signature = key.sign(hash);
335325

336326
// Verify the signature
337-
SM2Signer verifier = key.getSM2SignerForHash();
327+
SM2Signer verifier = key.getVerifier();
338328
boolean isValid = verifier.verifyHashSignature(hash, signature.r, signature.s);
339329

340330
assertTrue("Signature should be valid", isValid);

framework/src/test/java/org/tron/common/utils/PublicMethod.java

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -80,12 +80,6 @@ public static byte[] getSM2PublicKeyFromPrivate(String privateKey) {
8080
return SM2.publicKeyFromPrivate(tmpKey, true);
8181
}
8282

83-
public static byte[] getSM2HashByPubKey(byte[] pubKey, String message) {
84-
SM2 key = SM2.fromPublicOnly(pubKey);
85-
SM2Signer signer = key.getSM2SignerForHash();
86-
return signer.generateSM3Hash(message.getBytes());
87-
}
88-
8983
/** constructor. */
9084
public static SmartContractOuterClass.SmartContract.ABI jsonStr2Abi(String jsonStr) {
9185
if (jsonStr == null) {

0 commit comments

Comments
 (0)