According to the curve25519 paper a x25519 public key can be represented in 32 bytes.

The x25519 library I'm using (bouncycastle) however gives me a 33 byte representation according to this standard.

My 33 byte hex representation always begins with the hex 02 or 03.

How do I convert a 33 byte representation to a 32 byte representation?

And when I am provided with a 32 byte representation, how do I know whether to prepend 02 or 03 to it to convert it back to a 33 byte representation?


The second link you provided actually contains all the information you need:

Note that ECPoint.point differs from the definition of public keys in [Curve25519] in two ways: (1) the byte-ordering is big-endian, wich is more uniform with how big integers are represented in TLS, and (2) there is an additional length byte (so ECpoint.point is actually 33 bytes), again for uniformity (and extensibility).

Beside of those two difference, the rest is the same.

Now, from BouncyCastle code, you can see that:

PO[0] = (byte)(normed.getCompressionYTilde() ? 0x03 : 0x02);
System.arraycopy(X, 0, PO, 1, X.length);

So, you are right, it will always begin with 0x03 or 0x02 and the actual $X$ value is contained as being the 32 last bytes on the getEncoded() return value. (According to the draft you linked, it will probably be in big-endian, so you might need to convert them into little-endian depending on why/how you need them.)

| improve this answer | |
  • 2
    $\begingroup$ This is an OK answer, but it is clear that the "additional length byte" isn't present, so this is not according to the paper. What's generated by Bouncy Castle is a compressed point indicator for the Y value followed by an unsigned, big endian X coordinate. To convert to the format by Daniel, simply remove the first byte, then reverse the byte array now holding the X-coordinate. Note that Daniels paper is mainly concerned with speed on a little endian Pentium CPU. I'm not sure if it should be used for compatibility; most ECC specs use big endian notation for numbers. $\endgroup$ – Maarten Bodewes Sep 21 '17 at 22:18
  • $\begingroup$ @Maarten Right, I wondered why the length wasn't encoded as explained in the draft, but BC documentation is sadly more than lacking. Even more knowing that the X coordinate contains everything you need in the X25519 case. In the end, I believe this is most likely to have generic curve interoperability of that function. $\endgroup$ – Lery Sep 21 '17 at 22:40
  • $\begingroup$ If I want to convert from the 32 byte representation back to a 33 byte representation, can I just convert to big endian and prefix a 0x03? or should it be 0x02? $\endgroup$ – knaccc Sep 22 '17 at 5:57
  • $\begingroup$ Probably not directly as an ECPoint object, but as a byte input for the Curve25519 function there should probably be a way. $\endgroup$ – Lery Sep 22 '17 at 11:51
  • $\begingroup$ If I simply remove the leading 02/03 prefix, I don't know if that would be a correct answer to the question, because it misses the information required to convert it back to an EC point. If you don't mind, I'll leave this question without a selected answer for now, to see if anyone else can chime in. $\endgroup$ – knaccc Sep 22 '17 at 15:21

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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