Skip to content

Commit 36e0bb0

Browse files
authored
Merge pull request #54 from 3for/fix/pq-auth-sig-length-bound
fix(net): pq auth sig length bound
2 parents 6da7fbc + f8efbfd commit 36e0bb0

15 files changed

Lines changed: 629 additions & 30 deletions

File tree

actuator/src/main/java/org/tron/core/actuator/AccountPermissionUpdateActuator.java

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -70,9 +70,10 @@ public boolean execute(Object object) throws ContractExeException {
7070

7171
private boolean checkPermission(Permission permission) throws ContractValidateException {
7272
DynamicPropertiesStore dynamicStore = chainBaseManager.getDynamicPropertiesStore();
73-
if (permission.getKeysCount() > dynamicStore.getTotalSignNum()) {
73+
int totalSignNum = dynamicStore.getTotalSignNum();
74+
if (permission.getKeysCount() > totalSignNum) {
7475
throw new ContractValidateException("number of keys in permission should not be greater "
75-
+ "than " + dynamicStore.getTotalSignNum());
76+
+ "than " + totalSignNum);
7677
}
7778
if (permission.getKeysCount() == 0) {
7879
throw new ContractValidateException("key's count should be greater than 0");

actuator/src/main/java/org/tron/core/utils/TransactionUtil.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -201,8 +201,8 @@ public static Transaction truncateSignatures(Transaction trx) {
201201
public TransactionSignWeight getTransactionSignWeight(Transaction trx) {
202202
TransactionSignWeight.Builder tswBuilder = TransactionSignWeight.newBuilder();
203203
Result.Builder resultBuilder = Result.newBuilder();
204-
if (trx.getSignatureCount() + trx.getPqAuthSigCount()
205-
> chainBaseManager.getDynamicPropertiesStore().getTotalSignNum()) {
204+
int totalSignNum = chainBaseManager.getDynamicPropertiesStore().getTotalSignNum();
205+
if (trx.getSignatureCount() + trx.getPqAuthSigCount() > totalSignNum) {
206206
resultBuilder.setCode(Result.response_code.OTHER_ERROR);
207207
resultBuilder.setMessage("too many signatures");
208208
tswBuilder.setResult(resultBuilder);

chainbase/src/main/java/org/tron/core/capsule/BlockCapsule.java

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@
3434
import org.tron.common.bloom.Bloom;
3535
import org.tron.common.crypto.SignInterface;
3636
import org.tron.common.crypto.SignUtils;
37+
import org.tron.common.crypto.pqc.PQAuthSigValidator;
3738
import org.tron.common.crypto.pqc.PQSchemeRegistry;
3839
import org.tron.common.parameter.CommonParameter;
3940
import org.tron.common.utils.ByteArray;
@@ -259,9 +260,11 @@ private boolean validateLegacySignature(BlockHeader header, byte[] witnessPermis
259260
private boolean validatePQSignature(DynamicPropertiesStore dynamicPropertiesStore,
260261
byte[] witnessPermissionAddress, PQAuthSig pqAuthSig)
261262
throws ValidateSignatureException {
262-
/*
263-
Verify the PQ scheme is supported and proposal opened
264-
*/
263+
// Keep consensus and ingress handling of PQAuthSig wire fields aligned.
264+
if (PQAuthSigValidator.hasUnknownFields(pqAuthSig)) {
265+
throw new ValidateSignatureException("pq_auth_sig contains unknown fields");
266+
}
267+
265268
PQScheme scheme = pqAuthSig.getScheme();
266269
if (!dynamicPropertiesStore.isPqSchemeAllowed(scheme)) {
267270
throw new ValidateSignatureException("pq_auth_sig scheme " + scheme + " is not allowed");

chainbase/src/main/java/org/tron/core/capsule/TransactionCapsule.java

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@
4848
import org.tron.common.crypto.Rsv;
4949
import org.tron.common.crypto.SignInterface;
5050
import org.tron.common.crypto.SignUtils;
51+
import org.tron.common.crypto.pqc.PQAuthSigValidator;
5152
import org.tron.common.crypto.pqc.PQSchemeRegistry;
5253
import org.tron.common.es.ExecutorServiceManager;
5354
import org.tron.common.math.StrictMathWrapper;
@@ -242,7 +243,7 @@ public static long checkWeight(Permission permission, List<ByteString> sigs, byt
242243
long currentWeight = 0;
243244
if (sigs.size() > permission.getKeysCount()) {
244245
throw new PermissionException(
245-
"Signature count is " + (sigs.size()) + " more than key counts of permission : "
246+
"Signature count " + sigs.size() + " exceeds permission key count "
246247
+ permission.getKeysCount());
247248
}
248249
HashMap addMap = new HashMap();
@@ -684,7 +685,8 @@ public boolean validatePubSignature(AccountStore accountStore,
684685
if (signatureCount == 0 || this.transaction.getRawData().getContractCount() <= 0) {
685686
throw new ValidateSignatureException("miss sig or contract");
686687
}
687-
if (signatureCount > dynamicPropertiesStore.getTotalSignNum()) {
688+
int totalSignNum = dynamicPropertiesStore.getTotalSignNum();
689+
if (signatureCount > totalSignNum) {
688690
throw new ValidateSignatureException("too many signatures");
689691
}
690692

@@ -744,8 +746,20 @@ public static long validatePQSignatureGetWeight(Transaction transaction, Permiss
744746

745747
Set<ByteString> signedAddresses = new HashSet<>(approveList);
746748

749+
List<PQAuthSig> pqAuthSigList = transaction.getPqAuthSigList();
750+
// A PQ signer must map to a distinct permission key.
751+
if (pqAuthSigList.size() > permission.getKeysCount()) {
752+
throw new PermissionException(
753+
"pq_auth_sig count " + pqAuthSigList.size()
754+
+ " exceeds permission key count " + permission.getKeysCount());
755+
}
756+
747757
long weight = 0L;
748-
for (PQAuthSig witness : transaction.getPqAuthSigList()) {
758+
for (PQAuthSig witness : pqAuthSigList) {
759+
// Keep consensus and ingress handling of PQAuthSig wire fields aligned.
760+
if (PQAuthSigValidator.hasUnknownFields(witness)) {
761+
throw new SignatureFormatException("pq_auth_sig contains unknown fields");
762+
}
749763
PQScheme scheme = witness.getScheme();
750764
if (!dynamicPropertiesStore.isPqSchemeAllowed(scheme)) {
751765
throw new PermissionException(scheme + " is not allowed");
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
package org.tron.common.crypto.pqc;
2+
3+
import org.tron.protos.Protocol.PQAuthSig;
4+
import org.tron.protos.Protocol.PQScheme;
5+
6+
/**
7+
* Shared admission checks for {@link PQAuthSig}. Ingress paths use the bounded
8+
* size check; consensus paths also reject unknown fields to keep wire semantics
9+
* uniform.
10+
*/
11+
public final class PQAuthSigValidator {
12+
13+
private PQAuthSigValidator() {
14+
}
15+
16+
/** Returns {@code true} when nested unknown fields are present. */
17+
public static boolean hasUnknownFields(PQAuthSig pqAuthSig) {
18+
return pqAuthSig.getUnknownFields().getSerializedSize() != 0;
19+
}
20+
21+
/**
22+
* Bounds known fields for registered schemes and rejects nested unknown
23+
* fields. Unregistered schemes are invalid at admission.
24+
*/
25+
public static boolean isLengthWithinBounds(PQAuthSig pqAuthSig) {
26+
if (hasUnknownFields(pqAuthSig)) {
27+
return false;
28+
}
29+
PQScheme scheme = pqAuthSig.getScheme();
30+
if (!PQSchemeRegistry.contains(scheme)) {
31+
return false;
32+
}
33+
return pqAuthSig.getPublicKey().size() <= PQSchemeRegistry.getPublicKeyLength(scheme)
34+
&& pqAuthSig.getSignature().size() <= PQSchemeRegistry.getSignatureLength(scheme);
35+
}
36+
}

framework/src/main/java/org/tron/core/Wallet.java

Lines changed: 26 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,7 @@
111111
import org.tron.common.crypto.Hash;
112112
import org.tron.common.crypto.SignInterface;
113113
import org.tron.common.crypto.SignUtils;
114+
import org.tron.common.crypto.pqc.PQAuthSigValidator;
114115
import org.tron.common.parameter.CommonParameter;
115116
import org.tron.common.runtime.ProgramResult;
116117
import org.tron.common.runtime.vm.LogInfo;
@@ -228,6 +229,7 @@
228229
import org.tron.protos.Protocol.MarketOrderPairList;
229230
import org.tron.protos.Protocol.MarketPrice;
230231
import org.tron.protos.Protocol.MarketPriceList;
232+
import org.tron.protos.Protocol.PQAuthSig;
231233
import org.tron.protos.Protocol.Permission;
232234
import org.tron.protos.Protocol.Permission.PermissionType;
233235
import org.tron.protos.Protocol.Proposal;
@@ -509,6 +511,18 @@ public GrpcAPI.Return broadcastTransaction(Transaction signedTransaction) {
509511
trx.setTime(System.currentTimeMillis());
510512
Sha256Hash txID = trx.getTransactionId();
511513
try {
514+
// Bound signature entry count before per-entry validation.
515+
int totalSignCount = signedTransaction.getSignatureCount()
516+
+ signedTransaction.getPqAuthSigCount();
517+
int totalSignNum = chainBaseManager.getDynamicPropertiesStore().getTotalSignNum();
518+
if (totalSignCount > totalSignNum) {
519+
String info = "total signature count " + totalSignCount + " exceeds " + totalSignNum;
520+
logger.warn("Broadcast transaction {} has failed, {}.", txID, info);
521+
return builder.setResult(false).setCode(response_code.SIGERROR)
522+
.setMessage(ByteString.copyFromUtf8("Validate signature error: " + info))
523+
.build();
524+
}
525+
512526
for (ByteString sig : signedTransaction.getSignatureList()) {
513527
if (!SignUtils.isValidLength(sig.size())) {
514528
String info = "Signature size is " + sig.size();
@@ -519,6 +533,16 @@ public GrpcAPI.Return broadcastTransaction(Transaction signedTransaction) {
519533
}
520534
}
521535

536+
for (PQAuthSig pqAuthSig : signedTransaction.getPqAuthSigList()) {
537+
if (!PQAuthSigValidator.isLengthWithinBounds(pqAuthSig)) {
538+
String info = "pq_auth_sig size is out of bounds";
539+
logger.warn("Broadcast transaction {} has failed, {}.", txID, info);
540+
return builder.setResult(false).setCode(response_code.SIGERROR)
541+
.setMessage(ByteString.copyFromUtf8("Validate signature error: " + info))
542+
.build();
543+
}
544+
}
545+
522546
if (tronNetDelegate.isBlockUnsolidified()) {
523547
logger.warn("Broadcast transaction {} has failed, block unsolidified.", txID);
524548
return builder.setResult(false).setCode(response_code.BLOCK_UNSOLIDIFIED)
@@ -632,8 +656,8 @@ public TransactionApprovedList getTransactionApprovedList(Transaction trx) {
632656
TransactionApprovedList.Builder tswBuilder = TransactionApprovedList.newBuilder();
633657
TransactionApprovedList.Result.Builder resultBuilder = TransactionApprovedList.Result
634658
.newBuilder();
635-
if (trx.getSignatureCount() + trx.getPqAuthSigCount()
636-
> chainBaseManager.getDynamicPropertiesStore().getTotalSignNum()) {
659+
int totalSignNum = chainBaseManager.getDynamicPropertiesStore().getTotalSignNum();
660+
if (trx.getSignatureCount() + trx.getPqAuthSigCount() > totalSignNum) {
637661
resultBuilder.setCode(TransactionApprovedList.Result.response_code.OTHER_ERROR);
638662
resultBuilder.setMessage("too many signatures");
639663
tswBuilder.setResult(resultBuilder);

framework/src/main/java/org/tron/core/net/message/TronMessageFactory.java

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,13 @@ public static TronMessage create(byte[] data) throws Exception {
2626
boolean isException = false;
2727
try {
2828
byte type = data[0];
29+
// Reject oversized hellos before copying the body.
30+
if (type == MessageTypes.P2P_HELLO.asByte()
31+
&& data.length - 1 > HelloMessage.MAX_HELLO_MESSAGE_SIZE) {
32+
throw new P2pException(P2pException.TypeEnum.BAD_MESSAGE,
33+
"hello message size " + (data.length - 1) + " exceeds "
34+
+ HelloMessage.MAX_HELLO_MESSAGE_SIZE);
35+
}
2936
byte[] rawData = ArrayUtils.subarray(data, 1, data.length);
3037
return create(type, rawData);
3138
} catch (final P2pException e) {

framework/src/main/java/org/tron/core/net/message/handshake/HelloMessage.java

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,15 @@
33
import com.google.protobuf.ByteString;
44
import lombok.Getter;
55
import org.apache.commons.lang3.StringUtils;
6+
import org.tron.common.crypto.pqc.PQAuthSigValidator;
67
import org.tron.common.utils.ByteArray;
78
import org.tron.common.utils.DecodeUtil;
89
import org.tron.common.utils.Sha256Hash;
910
import org.tron.common.utils.StringUtil;
1011
import org.tron.core.ChainBaseManager;
1112
import org.tron.core.capsule.BlockCapsule;
1213
import org.tron.core.config.args.Args;
14+
import org.tron.core.exception.P2pException;
1315
import org.tron.core.net.message.MessageTypes;
1416
import org.tron.core.net.message.TronMessage;
1517
import org.tron.p2p.discover.Node;
@@ -23,16 +25,28 @@ public class HelloMessage extends TronMessage {
2325
@Getter
2426
private Protocol.HelloMessage helloMessage;
2527

28+
/** Raw inbound hello size cap, enforced before parsing. */
29+
public static final int MAX_HELLO_MESSAGE_SIZE = 16 * 1024;
30+
2631
public HelloMessage(byte type, byte[] rawData) throws Exception {
2732
super(type, rawData);
33+
checkRawSize(rawData);
2834
this.helloMessage = Protocol.HelloMessage.parseFrom(rawData);
2935
}
3036

3137
public HelloMessage(byte[] data) throws Exception {
3238
super(MessageTypes.P2P_HELLO.asByte(), data);
39+
checkRawSize(data);
3340
this.helloMessage = Protocol.HelloMessage.parseFrom(data);
3441
}
3542

43+
private static void checkRawSize(byte[] rawData) throws P2pException {
44+
if (rawData != null && rawData.length > MAX_HELLO_MESSAGE_SIZE) {
45+
throw new P2pException(P2pException.TypeEnum.BAD_MESSAGE,
46+
"hello message size " + rawData.length + " exceeds " + MAX_HELLO_MESSAGE_SIZE);
47+
}
48+
}
49+
3650
public HelloMessage(Node from, long timestamp, ChainBaseManager chainBaseManager) {
3751

3852
Endpoint fromEndpoint = getEndpointFromNode(from);
@@ -187,6 +201,12 @@ public boolean valid() {
187201
return false;
188202
}
189203

204+
// PQ signatures are bounded by scheme and reject nested unknown fields.
205+
if (this.helloMessage.hasPqAuthSig()
206+
&& !PQAuthSigValidator.isLengthWithinBounds(this.helloMessage.getPqAuthSig())) {
207+
return false;
208+
}
209+
190210
return true;
191211
}
192212

framework/src/main/java/org/tron/core/net/messagehandler/TransactionsMsgHandler.java

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
import org.springframework.beans.factory.annotation.Autowired;
1616
import org.springframework.stereotype.Component;
1717
import org.tron.common.crypto.SignUtils;
18+
import org.tron.common.crypto.pqc.PQAuthSigValidator;
1819
import org.tron.common.es.ExecutorServiceManager;
1920
import org.tron.common.prometheus.MetricKeys;
2021
import org.tron.common.prometheus.Metrics;
@@ -32,6 +33,7 @@
3233
import org.tron.core.net.peer.PeerConnection;
3334
import org.tron.core.net.service.adv.AdvService;
3435
import org.tron.protos.Protocol.Inventory.InventoryType;
36+
import org.tron.protos.Protocol.PQAuthSig;
3537
import org.tron.protos.Protocol.ReasonCode;
3638
import org.tron.protos.Protocol.Transaction;
3739
import org.tron.protos.Protocol.Transaction.Contract.ContractType;
@@ -154,12 +156,25 @@ private void check(PeerConnection peer, TransactionsMessage msg) throws P2pExcep
154156
throw new P2pException(TypeEnum.BAD_TRX,
155157
"tx " + item.getHash() + " contract size should be greater than 0");
156158
}
159+
// Bound signature entry count before per-entry validation.
160+
int sigCount = trx.getSignatureCount() + trx.getPqAuthSigCount();
161+
int totalSignNum = chainBaseManager.getDynamicPropertiesStore().getTotalSignNum();
162+
if (sigCount > totalSignNum) {
163+
throw new P2pException(TypeEnum.BAD_TRX, "tx " + item.getHash()
164+
+ " total signature count is " + sigCount + " exceeds " + totalSignNum);
165+
}
157166
for (ByteString sig : trx.getSignatureList()) {
158167
if (!SignUtils.isValidLength(sig.size())) {
159168
throw new P2pException(TypeEnum.BAD_TRX,
160169
"tx " + item.getHash() + " signature size is " + sig.size());
161170
}
162171
}
172+
for (PQAuthSig pqAuthSig : trx.getPqAuthSigList()) {
173+
if (!PQAuthSigValidator.isLengthWithinBounds(pqAuthSig)) {
174+
throw new P2pException(TypeEnum.BAD_TRX,
175+
"tx " + item.getHash() + " pq_auth_sig size is out of bounds");
176+
}
177+
}
163178
}
164179
}
165180

@@ -226,4 +241,4 @@ public TrxEvent(PeerConnection peer, TransactionMessage msg) {
226241
this.time = System.currentTimeMillis();
227242
}
228243
}
229-
}
244+
}

0 commit comments

Comments
 (0)