Repository: go
Issue: proposal: crypto/ecdh: allow external curve and key implementations
Labels: Proposal,Proposal-Crypto

### Proposal Details

At this point this is more a number of thoughts and description of issues I've encountered trying to work with `crypto/ecdh` rather than a proposal to change things in a specific way. `crypto/ecdh` is used by a lot of people, so breaking changes are out of the picture unless maybe if a `/v2` version is created, but I am not sure this is warranted or even qualifies.

My goal here is first to start a discussion.

Golang's `crypto` package is nice because objects like `crypto.PrivateKey` are interfaces that allows both the use of local secrets managed via the standard libraries, or hardware encryption where secrets aren't stored on the machine at all.

It seems that this was a goal, as `crypto/ecdh.Curve` is an interface that could be implemented by a third party package, for the exception that it has private functions and it is impossible to implement NewPrivateKey or NewPublicKey because both are opaque objects.

Ideally both `ecdh.PublicKey` and `ecdh.PrivateKey` would be interfaces, however now that the package is widely used it is not possible to change that without causing a lot of breakage. 

An alternative would be to have a method to create private/public key objects by specifying the data (bytes) and curve interface, and making the private elements of the curve interface public. Actually it would be better if the private key data could be an interface for the private key, as in the case of a tpm it would be a Handle, and in some cases libraries may prefer keeping track of specific values for performance.

Point is, the ecdh interface as it exists is very nice. Creating an ephemeral private key to encode a message to a recipient is as easy as recipient.Curve().GenerateKey(rand.Reader), it's a shame however that it is not possible to implement other curves. 

Current library has a nice API, the part about PrivateKey can actually be worked around fairly easily as long as the curve is one supported by `crypto/ecdh`. Working with other curves however is proving to be extremely difficult, dirty alternatives include things like implementing `GenerateKey` directly on the public key object that returns not a `*ecdh.PrivateKey` but a custom object that simply implements the minimum required.

Some suggestions here:

```go
type PrivateKeyInterface interface {
    Curve() Curve
    ECDH(remote *PublicKey) ([]byte, error)
    Equal(x crypto.PrivateKey) bool
    Public() crypto.PublicKey
}

type GenericCurve interface {
    // Using a different name so that to not break existing Curve
    GeneratePrivateKey(rand io.Reader) (PrivateKeyInterface, error)

    // The current ecdh package does not expose the following methods, making these public would allow custom implementations so I'm curious for the more specific reason than "breaking backward compatibility" for an interface that cannot be implemented externally anyway
    ECDH(local *PrivateKey, remote *PublicKey) ([]byte, error)
    PrivateKeyToPublicKey(PrivateKeyInterface) *PublicKey
}

// NewPublicKey would simply return a public key object
func NewPublicKey(c Curve, data []byte) *PublicKey

// This could be nice to allow custom curve objects to be marshalable, and maybe parsable if there is some method to register a type
type X509Marshalable interface {
    GetOID() pkix.AlgorithmIdentifier
    Bytes() []byte // current name in ecdh
}
```

More curves are being requested, and being able to extend things without the need for users of `crypto/ecdh` to think too much about makes sense I believe.

* https://github.com/golang/go/issues/59368 about support for adding a specific curve
* https://github.com/golang/go/issues/59783