Skip to content

Commit 7152abc

Browse files
committed
Fix RSA signature malleability
1 parent 6db0f74 commit 7152abc

File tree

2 files changed

+9075
-544
lines changed

2 files changed

+9075
-544
lines changed

include/ack/rsa.hpp

+58-56
Original file line numberDiff line numberDiff line change
@@ -43,28 +43,64 @@ namespace ack {
4343
};
4444
constexpr size_t pkcs1_v1_5_t_sha512_size = sha512_digest_info_prefix.size() + sizeof(hash512);
4545
static_assert( pkcs1_v1_5_t_sha512_size == 83 );
46-
}
4746

47+
/**
48+
* Functions check if signature representative s <= n - 1.
49+
* @note function expects s to bi positive integer.
50+
*
51+
* @param s - signature representative
52+
* @param n - public key modulus
53+
* @return true if s < n else false.
54+
*/
55+
inline bool is_s_valid(const bytes_view& s, std::span<const uint8_t> n) {
56+
// Skip leading zeros of s
57+
size_t sofs = 0;
58+
while (sofs < s.size() && s[sofs] == 0x00) {
59+
++sofs;
60+
}
4861

49-
void uECC_vli_clear(uint32_t *vli, uint32_t num_words) {
50-
uint32_t i;
51-
for (i = 0; i < num_words; ++i) {
52-
vli[i] = 0;
53-
}
54-
}
55-
void uECC_vli_bytesToNative(uint32_t *native,
56-
const uint8_t *bytes,
57-
int num_bytes) {
58-
int i;
59-
uECC_vli_clear(native, (num_bytes + (sizeof(uint32_t) - 1)) / sizeof(uint32_t));
60-
for (i = 0; i < num_bytes; ++i) {
61-
unsigned b = num_bytes - 1 - i;
62-
native[b / sizeof(uint32_t)] |=
63-
(uint32_t)bytes[i] << (8 * (b % sizeof(uint32_t)));
62+
// Check if s is zero
63+
if ( sofs == s.size() ) {
64+
return true;
65+
}
66+
67+
// Skip leading zeros of n
68+
size_t nofs = 0;
69+
while (nofs < n.size() && n[nofs] == 0x00) {
70+
++nofs;
71+
}
72+
73+
// Check if s is smaller than nofs
74+
if (sofs < nofs) {
75+
return true;
76+
}
77+
78+
// Check if s is greater than n
79+
if ( ( s.size() - sofs ) > ( n.size() - nofs ) ) {
80+
return false;
81+
}
82+
83+
// Check if s is smaller than n
84+
if ( ( s.size() - sofs ) < ( n.size() - nofs ) ) {
85+
return true;
86+
}
87+
88+
// Compare the remaining bytes (s.size() == n.size())
89+
#pragma unroll
90+
for (; sofs < s.size(); ++sofs, ++nofs) {
91+
if (s[sofs] < n[nofs]) {
92+
return true;
93+
}
94+
else if (s[sofs] > n[nofs]) {
95+
return false;
96+
}
97+
}
98+
99+
// s == n
100+
return false;
64101
}
65102
}
66103

67-
68104
/**
69105
* Returns result of modular exponentiation.
70106
* @param base - base byte array
@@ -105,42 +141,6 @@ namespace ack {
105141

106142
auto res = rsa_mod_exp_sw( (const uint8_t*)base, base_len, prop, (uint8_t *)out );
107143
rsa_free_key_prop( prop );
108-
109-
// assert(false);
110-
// using rsa_int = bigint<8192>;
111-
// rsa_int bn_base;
112-
113-
// // std::array<uint32_t, 32> aa_base;
114-
// // uECC_vli_bytesToNative(aa_base.data(), (const uint8_t*)base, base_len);
115-
// // bn_base.set_array(aa_base.data(), aa_base.size());
116-
117-
// if (!bn_base.set_bytes(bytes_view{ reinterpret_cast<const byte_t*>(base), base_len })) {
118-
// return 0;
119-
// }
120-
121-
// bigint<32> bn_exp;
122-
// // // std::array<uint32_t, 1> aa_exp;
123-
// // // uECC_vli_bytesToNative(aa_exp.data(), (const uint8_t*)exponent, exponent_len);
124-
// // // bn_exp.set_array(aa_exp.data(), aa_exp.size());
125-
// if (!bn_exp.set_bytes(bytes_view{ reinterpret_cast<const byte_t*>(exponent), exponent_len })) {
126-
// return 0;
127-
// }
128-
129-
// //constexpr word_t expp = 555;
130-
131-
// rsa_int bn_mod;
132-
// // std::array<uint32_t, 32> aa_mod;
133-
// // uECC_vli_bytesToNative(aa_mod.data(), (const uint8_t*)modulus, modulus_len);
134-
// // bn_mod.set_array(aa_mod.data(), aa_mod.size());
135-
// if (!bn_mod.set_bytes(bytes_view{ reinterpret_cast<const byte_t*>(modulus), modulus_len })) {
136-
// return 0;
137-
// }
138-
139-
// if (!bn_base.mod_exp(bn_exp, bn_mod)) {
140-
// return 0;
141-
// }
142-
143-
// return bn_base.get_bytes(std::span<byte_t>{ reinterpret_cast<byte_t*>(out), out_len }) ? base_len : 0;
144144
#endif
145145
return res == 0 ? base_len : 0;
146146
}
@@ -171,13 +171,15 @@ namespace ack {
171171
return result;
172172
}
173173

174-
[[nodiscard]] inline bytes rsavp1(const rsa_public_key_view pub_key, const bytes_view& signature) {
175-
// Note: Missing check for signature representative (integer between 0 and n - 1)
174+
[[nodiscard]] inline bytes rsavp1(const rsa_public_key_view pub_key, const bytes_view signature) {
175+
if ( !detail::is_s_valid( signature, pub_key.modulus ) ) {
176+
return bytes();
177+
}
176178
return powm( signature, pub_key.exponent, pub_key.modulus );
177179
}
178180

179181
template<size_t t_len, typename Lambda>
180-
[[nodiscard]] bool rsassa_pkcs1_v1_5_verify(const rsa_public_key_view& pub_key, const bytes_view& signature, Lambda&& gen_t)
182+
[[nodiscard]] bool rsassa_pkcs1_v1_5_verify(const rsa_public_key_view& pub_key, const bytes_view signature, Lambda&& gen_t)
181183
{
182184
if ( signature.size() != pub_key.modulus.size() ) {
183185
ACK_LOG_DEBUG( "[ERROR] rsassa_pkcs1_v1_5_verify: invalid signature" );

0 commit comments

Comments
 (0)