paulmillr / noble-curves

Audited & minimal JS implementation of elliptic curve cryptography.
https://paulmillr.com/noble
MIT License
664 stars 62 forks source link

How do I convert a curve point into a public key? #137

Closed AljoschaMeyer closed 4 months ago

AljoschaMeyer commented 4 months ago

If I have an x25519 curve point (say, returned from hashToCurve from "npm:@noble/curves/ed25519"), how do I convert it into a corresponding ed25519 public key? Does curvePoint.toRawBytes() yield the public key, or something else? I cannot find any documentation on what exactly toRawBytes actually returns.

nflatrea commented 4 months ago

Hi,

The curvePoint.toRawBytes() method does not directly yield the public key. It returns the raw bytes representation of the curve point.

To convert an x25519 curve point into a corresponding ed25519 public key, you need to perform a few additional steps. There are several public key encoding standards outthere, including X.509, SEC1, PKCS#1, PKCS#8, and raw byte representation.

The example I'll provide below uses the SEC1 standard.

1- First, ensure that the curve point is on the Ed25519 curve. The hashToCurve function from the @.***/curves/ed25519" package should guarantee this, but it's good to double-check.

2- The x-coordinate of the curve point is used as the public key in Ed25519. However, Ed25519 public keys are not just the raw x-coordinate bytes. They are prefixed with a single byte that encodes the sign of the corresponding y-coordinate. This is done to ensure that the public key can be deterministically derived from the private key.

3- To get the Ed25519 public key, you need to compute the y-coordinate from the x-coordinate (since you only have the x-coordinate), determine its sign, and then prefix the x-coordinate with the appropriate sign byte.

Unfortunately, I don't reckon that the noble-curves library provides such a built-in function to perform these steps. You may need to implement this functionality yourself or use another library that supports Ed25519 key generation.

I hope this helps! Let me know if you have any other questions.

Best regards,

De : Aljoscha Meyer @.> À : paulmillr/noble-curves @.> Sujet : [paulmillr/noble-curves] How do I convert a curve point into a public key? (Issue #137) Date : 14/05/2024 13:05:34 Europe/Paris Copie à : Subscribed @.***>

If I have an x25519 curve point (say, returned from hashToCurve from @.***/curves/ed25519"), how do I convert it into a corresponding ed25519 public key? Does curvePoint.toRawBytes() yield the public key, or something else? I cannot find any documentation on what exactly toRawBytes actually returns.

— Reply to this email directly, view it on GitHub, or unsubscribe. You are receiving this because you are subscribed to this thread.Message ID: @.***>

paulmillr commented 4 months ago
  1. x25519 (curve25519) is a different curve from ed25519. they are used for different cases
  2. “Does curvePoint.toRawBytes() yield the public key, or something else?” - nope. It simply returns the encoded point.
  3. hashToCurve does not really “encode to public key”. it encodes to point

can you clarify what’s your use case?

AljoschaMeyer commented 4 months ago

Thank you both for your replies 💜

@nflatrea Yes, if this is not implemented by noble, we'll just have to implement that part of the ed25519 spec ourselves. I was simply hoping to get around that.

@paulmillr Good point asking for clarification, We might not actually need this.

TLDR: We need to somehow transmit curve points over the wire, and converting them into public keys seems like a well-specified and widely-implemented choice.

There are two independent use-cases: We want to use curve25519 for a multiset-homomorphic hash function, and we want to use curve25519 to implement private equality testing.

For the homomorphic hash function, the underlying idea is simple: to hash a set of items, hash every individual item into the curve (via curve25519XMD:SHA-512_ELL2_RO, i.e., hashToCurve), then add the curve points together to obtain the hash of the set. But then, as a final step, we need to transmit the result over the wire. To do so, we need to encode the curve point into a sequence of bytes. My impression was that converting it into a public key would be a convenient choice of encoding, since that conversion would be part of the curve25519 spec, and hence widely implemented.

In principle, we could also just use noble's toRawBytes, but we would need to specify the exact behavior, not just refer to a javascript function that does not precisely specify what it does - this is for writing a specification (draft state), not just for hacking on a codebase.

For the privat equality testing part, two peers need to exchange curve points and perform scalar multiplication on them, which again requires encoding them over the wire. Since crypto libraries often implement scalar multiplication functions that take an encoded public key as input (or at least, some raw byte array and not a curve point struct, compare, e.g., libsodium scalar_mult), it again seemed sensible to simply exchange public keys over the wire. But before we can do that, we must first hash the items for which we want to test for set intersection into the curve. Which is again a job for RFC9380, but leaves us with a curve point that we now need to encode into a public key to send to the other peer.

Here again, we don't necessarily need to use public keys. We could also specify our own encoding of curve points, send that over the wire, and the other peer could decode and do scalar multiplication (with APIs that operate on actual curve points and scalars, not keys).

paulmillr commented 4 months ago

well, all ed25519 pubkeys are encoded in a way that is specified in toRawBytes. so it is a “public key”. it’s just that a concept of public key is different from a concept of point. public key has a specific private key

but again you need curve25519, not ed25519