What changed, and why it matters
This commit changes three special cryptographic constants—named T, U, and V—used in Monero's upcoming FCMP++ privacy protocol. These constants are like fixed 'reference points' on a mathematical curve that the protocol relies on to prove transactions are valid without revealing who sent what. The old values were apparently copied from an outdated source and were wrong; the new values match the updated reference implementation. If the wrong constants had gone live, Monero's privacy proofs could have been broken or forged, potentially allowing someone to create fake coins or trace transactions. The commit itself only updates the byte strings and their sanity-check first bytes; it does not explain how the error was discovered or whether any real funds were at risk.
Treat this as a high-priority cryptographic correction. Independently re-derive the new T/U/V values from the documented hash-to-curve procedure (unbiased_hash_to_ec(Keccak256(domain label))) and compare against both the monero-oxide reference and the committed bytes. Add regression tests that re-derive and verify every generator at build or test time, and require two-party review for any future change to cryptographic constants. If these wrong values were ever present in a released or testnet build, assess whether any proofs or transactions need to be invalidated or rescanned.
Security signals we found
Cryptographic generator constants changed without explanation of root cause
Old constants sourced from an outdated third-party reference
New constants sourced from a newer third-party reference
No unit test or verification code added in the diff
Static sanity checks updated to match new first bytes, but no full-curve validation shown
Potential impact on FCMP++ proof soundness/privacy if wrong values were deployed
Evidence from the diff
The patch updates the raw compressed-point byte arrays for FCMP++ generators T, U, and V in src/crypto/generators.cpp. The previous values were taken from an older monero-oxide commit (0e438ae); the new values are taken from a later monero-oxide commit (31c26d9) and update the compile-time static_assert first-byte checks accordingly. These generators are produced by unbiased_hash_to_ec over domain-separated Keccak256 strings. Using incorrect generators would break the soundness and/or privacy assumptions of the FCMP++ proof system, because the protocol’s algebraic relations are defined with respect to these exact points. The diff is a pure constant replacement; there is no accompanying test, proof-of-correctness, or disclosure narrative in the supplied materials.
Changed components
src/crypto/generators.cppFCMP++ generator TFCMP++ generator UFCMP++ generator VMonero ring signature / proof-of-payment code paths that load these generatorsInspect captured patch +12 / −12
diff --git a/src/crypto/generators.cpp b/src/crypto/generators.cpp
index 77200a6..80c6e9f 100644
--- a/src/crypto/generators.cpp
+++ b/src/crypto/generators.cpp
@@ -68,17 +68,17 @@ constexpr public_key G = bytes_to<public_key>({ 0x58, 0x66, 0x66, 0x66, 0x66, 0x
constexpr public_key H = bytes_to<public_key>({ 0x8b, 0x65, 0x59, 0x70, 0x15, 0x37, 0x99, 0xaf, 0x2a, 0xea, 0xdc, 0x9f, 0xf1,
0xad, 0xd0, 0xea, 0x6c, 0x72, 0x51, 0xd5, 0x41, 0x54, 0xcf, 0xa9, 0x2c, 0x17, 0x3a, 0x0d, 0xd3, 0x9c, 0x1f, 0x94 });
//FCMP++ generator T: unbiased_hash_to_ec(Keccak256("Monero Generator T"))
-//Source: https://github.com/monero-oxide/monero-oxide/blob/0e438aed2cce5c0ab8a935916c1a89bb0077f97f/monero-oxide/ed25519/src/compressed_point.rs#L76-L79
-constexpr public_key T = bytes_to<public_key>({ 97, 183, 54, 206, 147, 182, 42, 61, 55, 120, 171, 32, 77, 168, 93, 59, 76,
- 220, 7, 37, 15, 93, 167, 227, 223, 38, 41, 146, 129, 52, 213, 38 });
+//Source: https://github.com/monero-oxide/monero-oxide/blob/31c26d96eaadbba910ffe3613ad8b4cf9c598a93/monero-oxide/ed25519/src/compressed_point.rs#L74-L79
+constexpr public_key T = bytes_to<public_key>({ 220, 66, 225, 211, 48, 123, 45, 75, 59, 2, 114, 154, 190, 87, 126, 35,
+ 29, 121, 71, 129, 65, 203, 91, 49, 12, 169, 250, 110, 18, 118, 22, 163});
//FCMP++ generator U: unbiased_hash_to_ec(Keccak256("Monero FCMP++ Generator U"))
-//Source: https://github.com/monero-oxide/monero-oxide/blob/0e438aed2cce5c0ab8a935916c1a89bb0077f97f/monero-oxide/ringct/fcmp%2B%2B/generators/src/lib.rs#L16-L18
-constexpr public_key U = bytes_to<public_key>({ 80, 107, 35, 246, 214, 229, 48, 153, 122, 188, 172, 198, 253, 52, 119, 52,
- 177, 76, 43, 215, 155, 234, 0, 238, 176, 72, 87, 232, 234, 221, 26, 138 });
+//Source: https://github.com/monero-oxide/monero-oxide/blob/31c26d96eaadbba910ffe3613ad8b4cf9c598a93/monero-oxide/ringct/fcmp%2B%2B/generators/src/lib.rs#L16-L24
+constexpr public_key U = bytes_to<public_key>({ 138, 148, 142, 40, 84, 7, 58, 160, 188, 184, 47, 134, 60, 128, 134, 91,
+ 92, 201, 190, 23, 151, 35, 252, 28, 191, 28, 37, 184, 133, 89, 126, 84});
//FCMP++ generator V: unbiased_hash_to_ec(Keccak256("Monero FCMP++ Generator V"))
-//Source: https://github.com/monero-oxide/monero-oxide/blob/0e438aed2cce5c0ab8a935916c1a89bb0077f97f/monero-oxide/ringct/fcmp%2B%2B/generators/src/lib.rs#L20-L22
-constexpr public_key V = bytes_to<public_key>({ 105, 53, 244, 19, 248, 49, 9, 19, 138, 122, 20, 180, 9, 85, 45, 59, 118,
- 216, 143, 202, 129, 187, 89, 39, 233, 161, 225, 48, 205, 254, 41, 249 });
+//Source: https://github.com/monero-oxide/monero-oxide/blob/31c26d96eaadbba910ffe3613ad8b4cf9c598a93/monero-oxide/ringct/fcmp%2B%2B/generators/src/lib.rs#L26-L34
+constexpr public_key V = bytes_to<public_key>({26, 66, 53, 9, 247, 103, 94, 145, 32, 17, 209, 75, 86, 16, 168, 87, 221,
+ 213, 136, 115, 52, 19, 181, 21, 224, 3, 188, 64, 85, 133, 91, 241,});
static ge_p3 G_p3;
static ge_p3 H_p3;
static ge_p3 T_p3;
@@ -193,9 +193,9 @@ static void init_gens()
// sanity check the generators
static_assert(static_cast<unsigned char>(G.data[0]) == 0x58, "compile-time constant sanity check");
static_assert(static_cast<unsigned char>(H.data[0]) == 0x8b, "compile-time constant sanity check");
- static_assert(static_cast<unsigned char>(T.data[0]) == 0x61, "compile-time constant sanity check");
- static_assert(static_cast<unsigned char>(U.data[0]) == 0x50, "compile-time constant sanity check");
- static_assert(static_cast<unsigned char>(V.data[0]) == 0x69, "compile-time constant sanity check");
+ static_assert(static_cast<unsigned char>(T.data[0]) == 0xdc, "compile-time constant sanity check");
+ static_assert(static_cast<unsigned char>(U.data[0]) == 0x8a, "compile-time constant sanity check");
+ static_assert(static_cast<unsigned char>(V.data[0]) == 0x1a, "compile-time constant sanity check");
// build ge_p3 representations of generators
const int G_deserialize = ge_frombytes_vartime(&G_p3, to_bytes(G));
Why this scored 78/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.