refactor(core): reimplement `math.log()` in `bulletproof` module
What changed, and why it matters
This commit replaces the standard Python math.log() function with a custom integer-only log2 function inside the Monero bulletproof code on Trezor devices. The change appears to be a cleanup or performance refactor to avoid using floating-point math in cryptographic code. There is no direct evidence in the commit that this fixes an active security vulnerability, but using integer math instead of floating-point approximations in cryptography is generally considered safer and more predictable.
No immediate action required. Treat as a defensive hardening/refactor. If auditing, verify that _log2() is only called with positive integers and that behavior matches the prior math.log() usage for all reachable inputs.
Security signals we found
Replaces floating-point math.log() with deterministic integer bit-shift logic in cryptographic scalar exponentiation
Adds input validation (assert n > 0) to new helper
Adds unit tests for boundary and invalid inputs
No changelog entry, consistent with internal refactor
Evidence from the diff
The patch removes the import of math and the call to math.log(n, 2) in _sc_square_mult(), substituting a new _log2(n) helper that computes the integer floor of log base 2 by right-shifting until n reaches zero. The function asserts n > 0 and returns -1 for n == 0 (though the caller already handles n == 0 separately). Tests are added covering small values, powers of two, and negative/zero inputs. The change is behavior-preserving for the positive-integer inputs used in the bulletproof module.
Changed components
core/src/apps/monero/xmr/bulletproof.pycore/tests/test_apps.monero.bulletproof.pyInspect captured patch +41 / −3
diff --git a/core/src/apps/monero/xmr/bulletproof.py b/core/src/apps/monero/xmr/bulletproof.py
index 90a79b7b..6d5d4a51 100644
--- a/core/src/apps/monero/xmr/bulletproof.py
+++ b/core/src/apps/monero/xmr/bulletproof.py
@@ -286,12 +286,10 @@ def _get_exponent_univ(dst, base, idx, salt):
def _sc_square_mult(dst: Scalar | None, x: Scalar, n: int) -> Scalar:
- import math
-
if n == 0:
return decodeint_into_noreduce(dst, _ONE)
- lg = int(math.log(n, 2))
+ lg = _log2(n)
dst = sc_copy(dst, x)
for i in range(1, lg + 1):
sc_mul_into(dst, dst, dst)
@@ -300,6 +298,20 @@ def _sc_square_mult(dst: Scalar | None, x: Scalar, n: int) -> Scalar:
return dst
+def _log2(n: int) -> int:
+ """
+ Replaces `math.log(n, 2)` for positive `n`.
+ """
+ assert n > 0
+
+ lg = -1
+ while n > 0:
+ n >>= 1
+ lg += 1
+
+ return lg
+
+
def _invert_batch(x):
scratch = _ensure_dst_keyvect(None, len(x))
acc = bytearray(_ONE)
diff --git a/core/tests/test_apps.monero.bulletproof.py b/core/tests/test_apps.monero.bulletproof.py
index d94cbbde..2035ddca 100644
--- a/core/tests/test_apps.monero.bulletproof.py
+++ b/core/tests/test_apps.monero.bulletproof.py
@@ -289,6 +289,32 @@ class TestMoneroBulletproof(unittest.TestCase):
proof = bpi.prove_batch(sv, gamma)
bpi.verify_batch([proof])
+ def test_log2(self):
+ for x, y in [
+ (1, 0),
+ (2, 1),
+ (3, 1),
+ (4, 2),
+ (5, 2),
+ (6, 2),
+ (7, 2),
+ (8, 3),
+ (9, 3),
+ ]:
+ self.assertEqual(bp._log2(x), y)
+
+ for y in range(2, 100):
+ x = 2**y
+ self.assertEqual(bp._log2(x - 2), y - 1)
+ self.assertEqual(bp._log2(x - 1), y - 1)
+ self.assertEqual(bp._log2(x), y)
+ self.assertEqual(bp._log2(x + 1), y)
+ self.assertEqual(bp._log2(x + 2), y)
+
+ for x in [0, -1, -2, -3, -10, -100, -1000]:
+ with self.assertRaises(AssertionError):
+ bp._log2(x)
+
if __name__ == "__main__":
unittest.main()
Why this scored 18/100
Community notes
Notes can correct, qualify, or add evidence to the AI analysis. Every note shown here has been validated by a human moderator.
The AI analysis stands alone for now. Submit a note if you can add evidence or important context.