I'm reading the RingCT whitepaper by Shen Noether. The scheme requires a second random generator H such that its discrete logarithm w.r.t. G is not known. Paragraph 3.2 suggests two variants to generate H:

1. H = toPoint(cn_fast_hash(G)) 
2. H = toPoint(cn_fast_hash(123456*G))

The first option is supported by the comments in rctTypes.h. The function toPoint isn't defined, neither in the article nor in the Monero source code, but I assume it is the rct::hashToPoint function.

1. 80f9755245adc94e9f3a1bf9b891ba515b3e6ed324b61b350b8918da59c9d5fd 
2. 24a1d0d7e659e986f31dae8d9a80234f518f6eb50346b04c98ca44df0f51c1e2

Both of them are not equal to the actual rct::H used in the official Monero code (again see rctTypes.h):


I thought that rct::hashToPoint already contains the hash function and I need to just call H = rct::hashToPoint(G), but I again got different results:

rct::hashToPoint(G) = d6329b5b1f7c0805b5c345f4957554002a2f557845f64d7645dae0e051a6498a
rct::hashToPoint(123456*G) = 89c0517d869e740d47429b10b642137ef7c789cc6d4dbcf6293e5a18c6044d48

It is crucial for Monero's security model that no one knows the discrete logarithm of H w.r.t. G. I assume that the code uses the hash-to-point from some modified version of G, e.g. G times some scalar or with some prefix/postfix string, but I failed to get any info about that.

Recap, how exactly was H computed?

  • When doing 123456∙G which function are you using?
    – jtgrassie
    Commented Jan 18, 2019 at 19:43
  • I think there have historically been 2 different Hp() implementations, which has caused this confusion. This is definitely something important to get to the bottom of.
    – knaccc
    Commented Jan 18, 2019 at 22:19
  • 1
    I've tracked it down. See page 15 in the Quarkslab bulletproof audit
    – jtgrassie
    Commented Jan 18, 2019 at 23:21

1 Answer 1


H can be verified by seeing how it's computed in the unit tests:

key G = scalarmultBase(d2h(1));
key H = hashToPointSimple(G);

When conducting the Bulletproof audit, the QuarksLab team dug into the verification of H also and documented this in section 4.3.4 (pages 15 and 16) of their report.

  • Thanks so much for tracking this down. The summary is: H = 8 * (the bytes of keccak(G) interpreted as an EC point). The multiplication by 8 ensures that the result is in the subgroup of the base point G.
    – knaccc
    Commented Jan 19, 2019 at 0:53
  • No problem at all. I'll admit I started getting a little nervous while looking for the proof! There were several different references for the construction of the G being hashed (G, 123456G and 1G).
    – jtgrassie
    Commented Jan 19, 2019 at 1:12
  • 1
    Btw when you interpret any random sequence of 32 bytes, about 50% will be a valid EC point when you attempt to interpret it as an EC point. So the people that wrote hashToPointSimple got lucky that it worked for interpreting keccak(G). For example, hashToPointSimple works for G, 2G, 4G, 5G, 9G and 10G, but not for 3G, 6G, 7G, or 8G. This explains why we ended up with a different hashToPoint function later on for RingCT.
    – knaccc
    Commented Jan 19, 2019 at 7:02

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.