Skip to content

Commit 67fa2ae

Browse files
Merge sergei's branch
2 parents 85ceb2d + ae91e9f commit 67fa2ae

File tree

6 files changed

+84
-10
lines changed

6 files changed

+84
-10
lines changed

src/PrivateKey.cpp

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -256,10 +256,9 @@ PublicKey PrivateKey::getPublicKey(TWPublicKeyType type) const {
256256
}
257257

258258
int ecdsa_sign_digest_checked(const ecdsa_curve* curve, const uint8_t* priv_key, const uint8_t* digest, size_t digest_size, uint8_t* sig, uint8_t* pby, int (*is_canonical)(uint8_t by, uint8_t sig[64])) {
259-
if (digest_size < 32) {
259+
if (digest_size != PrivateKey::ecdsaMessageSize) {
260260
return -1;
261261
}
262-
assert(digest_size >= 32);
263262
return ecdsa_sign_digest(curve, priv_key, digest, sig, pby, is_canonical);
264263
}
265264

src/PrivateKey.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@ class PrivateKey {
2020
static const size_t _size = 32;
2121
/// The number of bytes in a Cardano key (two extended ed25519 keys + chain code)
2222
static const size_t cardanoKeySize = 2 * 3 * 32;
23+
/// The number of bytes in an ECDSA message digest (e.g., 32-byte hash) that can be signed.
24+
static const size_t ecdsaMessageSize = 32;
2325

2426
/// The private key bytes:
2527
/// - common case: 'size' bytes

src/PublicKey.cpp

Lines changed: 30 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -20,9 +20,7 @@
2020

2121
namespace TW {
2222

23-
static constexpr size_t kEcdsaMinDigestSize = 32;
24-
25-
bool validateSignatureLength(TWPublicKeyType type, const Data& signature) {
23+
static bool validateSignatureLength(TWPublicKeyType type, const Data& signature) {
2624
switch (type) {
2725
case TWPublicKeyTypeSECP256k1:
2826
case TWPublicKeyTypeSECP256k1Extended:
@@ -37,6 +35,27 @@ bool validateSignatureLength(TWPublicKeyType type, const Data& signature) {
3735
}
3836
}
3937

38+
static bool validateMessageLength(TWPublicKeyType type, const Data& message) {
39+
switch (type) {
40+
case TWPublicKeyTypeED25519:
41+
case TWPublicKeyTypeCURVE25519:
42+
case TWPublicKeyTypeED25519Blake2b:
43+
case TWPublicKeyTypeED25519Cardano:
44+
// Allow any message size for ed25519.
45+
return true;
46+
case TWPublicKeyTypeSECP256k1:
47+
case TWPublicKeyTypeNIST256p1:
48+
case TWPublicKeyTypeSECP256k1Extended:
49+
case TWPublicKeyTypeNIST256p1Extended:
50+
return message.size() == PublicKey::ecdsaMessageSize;
51+
case TWPublicKeyTypeStarkex:
52+
// Digest shorter than 32 bytes will be left-padded with zeros before verification.
53+
return message.size() <= PublicKey::starkexMessageMaxSize;
54+
default:
55+
return false;
56+
}
57+
}
58+
4059
/// Determines if a collection of bytes makes a valid public key of the
4160
/// given type.
4261
bool PublicKey::isValid(const Data& data, enum TWPublicKeyType type) {
@@ -167,15 +186,16 @@ bool PublicKey::verify(const Data& signature, const Data& message) const {
167186
if (!validateSignatureLength(type, signature)) {
168187
return false;
169188
}
189+
if (!validateMessageLength(type, message)) {
190+
return false;
191+
}
170192

171193
switch (type) {
172194
case TWPublicKeyTypeSECP256k1:
173195
case TWPublicKeyTypeSECP256k1Extended:
174-
if (message.size() < kEcdsaMinDigestSize) { return false; }
175196
return ecdsa_verify_digest(&secp256k1, bytes.data(), signature.data(), message.data()) == 0;
176197
case TWPublicKeyTypeNIST256p1:
177198
case TWPublicKeyTypeNIST256p1Extended:
178-
if (message.size() < kEcdsaMinDigestSize) { return false; }
179199
return ecdsa_verify_digest(&nist256p1, bytes.data(), signature.data(), message.data()) == 0;
180200
case TWPublicKeyTypeED25519:
181201
return ed25519_sign_open(message.data(), message.size(), bytes.data(), signature.data()) == 0;
@@ -187,7 +207,7 @@ bool PublicKey::verify(const Data& signature, const Data& message) const {
187207
}
188208
case TWPublicKeyTypeCURVE25519: {
189209
auto ed25519PublicKey = Data();
190-
ed25519PublicKey.resize(PublicKey::ed25519Size);
210+
ed25519PublicKey.resize(ed25519Size);
191211
curve25519_pk_to_ed25519(ed25519PublicKey.data(), bytes.data());
192212

193213
ed25519PublicKey[31] &= 0x7F;
@@ -207,10 +227,13 @@ bool PublicKey::verify(const Data& signature, const Data& message) const {
207227
}
208228

209229
bool PublicKey::verifyAsDER(const Data& signature, const Data& message) const {
230+
if (message.size() != ecdsaMessageSize) {
231+
return false;
232+
}
233+
210234
switch (type) {
211235
case TWPublicKeyTypeSECP256k1:
212236
case TWPublicKeyTypeSECP256k1Extended: {
213-
if (message.size() < kEcdsaMinDigestSize) { return false; }
214237
Data sig(64);
215238
int ret = ecdsa_sig_from_der(signature.data(), signature.size(), sig.data());
216239
if (ret) {

src/PublicKey.h

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,9 +37,15 @@ class PublicKey {
3737
/// The number of bytes in a secp256k1 signature.
3838
static const size_t secp256k1SignatureSize = 65;
3939

40-
/// Magic number used in V compnent encoding
40+
/// Magic number used in V component encoding
4141
static const byte SignatureVOffset = 27;
4242

43+
/// The number of bytes in an ECDSA message digest (e.g., 32-byte hash) that can be verified.
44+
static const size_t ecdsaMessageSize = 32;
45+
/// The maximum number of bytes in a message that can be verified with a starkex public key.
46+
/// Digest shorter than 32 bytes will be left-padded with zeros before verification.
47+
static const size_t starkexMessageMaxSize = 32;
48+
4349
/// The public key bytes.
4450
Data bytes;
4551

tests/common/PrivateKeyTests.cpp

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -323,6 +323,24 @@ TEST(PrivateKey, SignShortDigest) {
323323
}
324324
}
325325

326+
TEST(PrivateKey, SignLongDigest) {
327+
Data privKeyData = parse_hex("afeefca74d9a325cf1d6b6911d61a65c32afa8e02bd5e78e2e4ac2910bab45f5");
328+
auto privateKey = PrivateKey(privKeyData);
329+
Data shortDigest = TW::data("123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef012");
330+
{
331+
Data actual = privateKey.sign(shortDigest, TWCurveSECP256k1);
332+
EXPECT_EQ(actual.size(), 0ul);
333+
}
334+
{
335+
Data actual = privateKey.sign(shortDigest, TWCurveNIST256p1);
336+
EXPECT_EQ(actual.size(), 0ul);
337+
}
338+
{
339+
Data actual = privateKey.sign(shortDigest, TWCurveSECP256k1, isCanonical);
340+
EXPECT_EQ(actual.size(), 0ul);
341+
}
342+
}
343+
326344
TEST(PrivateKey, SignWithDifferentCurveWorks) {
327345
Data privKeyData = parse_hex("afeefca74d9a325cf1d6b6911d61a65c32afa8e02bd5e78e2e4ac2910bab45f5");
328346
// Using the deprecated constructor without specifying a curve

tests/interface/TWPublicKeyTests.cpp

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,19 @@ TEST(TWPublicKeyTests, VerifyInvalidLength) {
9191
ASSERT_FALSE(TWPublicKeyVerify(publicKey.get(), signature.get(), message.get()));
9292
}
9393

94+
TEST(TWPublicKeyTests, VerifyInvalidMessageLength) {
95+
const PrivateKey key(parse_hex("afeefca74d9a325cf1d6b6911d61a65c32afa8e02bd5e78e2e4ac2910bab45f5"), TWCurveSECP256k1);
96+
const auto privateKey = WRAP(TWPrivateKey, new TWPrivateKey{ key });
97+
98+
// Invalid message length - 31 bytes instead of 32
99+
const auto invalidMessage = DATA("de4e9524586d6fce45667f9ff12f661e79870c4105fa0fb58af976619bb114");
100+
// Valid 64-byte signature
101+
const auto signature = DATA("00000000000000000000000000000000000000000000000000000000000000020123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef80");
102+
103+
auto publicKey = WRAP(TWPublicKey, TWPrivateKeyGetPublicKeySecp256k1(privateKey.get(), false));
104+
ASSERT_FALSE(TWPublicKeyVerify(publicKey.get(), signature.get(), invalidMessage.get()));
105+
}
106+
94107
TEST(TWPublicKeyTests, VerifyAsDER) {
95108
const PrivateKey key = PrivateKey(parse_hex("afeefca74d9a325cf1d6b6911d61a65c32afa8e02bd5e78e2e4ac2910bab45f5"), TWCurveSECP256k1);
96109
const auto privateKey = WRAP(TWPrivateKey, new TWPrivateKey{ key });
@@ -108,6 +121,19 @@ TEST(TWPublicKeyTests, VerifyAsDER) {
108121
ASSERT_FALSE(TWPublicKeyVerify(publicKey.get(), signature.get(), digest.get()));
109122
}
110123

124+
TEST(TWPublicKeyTests, VerifyAsDERInvalidMessageLength) {
125+
const PrivateKey key = PrivateKey(parse_hex("afeefca74d9a325cf1d6b6911d61a65c32afa8e02bd5e78e2e4ac2910bab45f5"), TWCurveSECP256k1);
126+
const auto privateKey = WRAP(TWPrivateKey, new TWPrivateKey{ key });
127+
128+
// Invalid message length - 31 bytes instead of 32
129+
const auto invalidMessage = DATA("de4e9524586d6fce45667f9ff12f661e79870c4105fa0fb58af976619bb114");
130+
// Valid 64-byte signature
131+
const auto signature = DATA("00000000000000000000000000000000000000000000000000000000000000020123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef80");
132+
133+
auto publicKey = WRAP(TWPublicKey, TWPrivateKeyGetPublicKeySecp256k1(privateKey.get(), false));
134+
ASSERT_FALSE(TWPublicKeyVerifyAsDER(publicKey.get(), signature.get(), invalidMessage.get()));
135+
}
136+
111137
TEST(TWPublicKeyTests, VerifyEd25519) {
112138
const PrivateKey key(parse_hex("afeefca74d9a325cf1d6b6911d61a65c32afa8e02bd5e78e2e4ac2910bab45f5"));
113139
const auto privateKey = WRAP(TWPrivateKey, new TWPrivateKey{ key });

0 commit comments

Comments
 (0)