Skip to content

crypto/ecdh: new package #52221

Closed
Closed
@FiloSottile

Description

@FiloSottile

According to the Debian and the Google internal code search, crypto/elliptic is used almost exclusively as part of ECDSA (via crypto/ecdsa) and ECDH. It is however a very low-level and unsafe API for ECDH.

As part of an effort to move math/big outside the security perimeter, I have been moving the NIST curve implementations to a safe API in the nistec package (#52182). The nistec API is safe but still lower-level than necessary.

ECDH is used in TLS, SSH, JOSE, OpenPGP, PIV, and HPKE, as well as a component of other various ECIES schemes. There are a myriad of standards (ISO, NIST, ANSI, SECG, IETF) but thankfully they all work the same for NIST P curves at the ECDH level.

I'm proposing adding a new crypto/ecdh package that exposes a safe, []byte-based API for ECDH.

Between this package and crypto/ecdsa, there should be no need for direct uses of crypto/elliptic, and the big.Int-based methods of elliptic.Curve (ScalarMult, ScalarBaseMult, Add, Double, IsOnCurve) can be deprecated.

Below is the proposed API. Here are the motivating design goals of the API:

  • Most direct uses of crypto/elliptic can be replaced with it.
  • No support for custom curves.
    • Ideally, X25519 can be supported alongside NIST curves.
  • It can be implemented entirely in constant time.
  • Invalid states can't be represented.
    • For example, it's not possible to provide an invalid point to the scalar multiplication.
  • Errors are returned for every invalid input.
  • Using one curve does not make the other curve implementations reachable.
    • This helps both with binary size and with govulncheck accuracy.
  • PublicKey and PrivateKey are compatible with analogous types in other packages.
    • This is why PrivateKey has a Public() crypto.PublicKey method.
  • It's possible to add any additional methods we realized might be necessary later.
    • This is why the main interface has a private method, so it can't be implemented by external types and can be extended while retaining backwards compatibility.
  • In addition to the standard ECDH flow, it's possible to validate a public key, and to convert a private key to a public key.
  • With collaboration from the compiler it's possible to use the API with zero allocations.

/cc @golang/security @golang/proposal-review


package ecdh

type Curve interface {
	// ECDH performs a ECDH exchange and returns the shared secret.
	//
	// For NIST curves, this performs ECDH as specified in SEC 1, Version 2.0,
	// Section 3.3.1, and returns the x-coordinate encoded according to SEC 1,
	// Version 2.0, Section 2.3.5. In particular, if the result is the point at
	// infinity, ECDH returns an error. (Note that for NIST curves, that's only
	// possible if the private key is the all-zero value.)
	//
	// For X25519, this performs ECDH as specified in RFC 7748, Section 6.1. If
	// the result is the all-zero value, ECDH returns an error.
	ECDH(local *PrivateKey, remote *PublicKey) ([]byte, error)

	// GenerateKey generates a new PrivateKey from rand.
	GenerateKey(rand io.Reader) (*PrivateKey, error)

	// NewPrivateKey checks that key is valid and returns a PrivateKey.
	//
	// For NIST curves, this follows SEC 1, Version 2.0, Section 2.3.6, which
	// amounts to decoding the bytes as a fixed length big endian integer and
	// checking that the result is lower than the order of the curve. The zero
	// private key is also rejected, as the encoding of the corresponding public
	// key would be irregular.
	//
	// For X25519, this only checks the scalar length. Adversarially selected
	// private keys can cause ECDH to return an error.
	NewPrivateKey(key []byte) (*PrivateKey, error)

	// NewPublicKey checks that key is valid and returns a PublicKey.
	//
	// For NIST curves, this decodes an uncompressed point according to SEC 1,
	// Version 2.0, Section 2.3.4. Compressed encodings and the point at
	// infinity are rejected.
	//
	// For X25519, this only checks the u-coordinate length. Adversarially
	// selected public keys can cause ECDH to return an error.
	NewPublicKey(key []byte) (*PublicKey, error)

	// Has unexported methods.
}

func P256() Curve
func P384() Curve
func P521() Curve
func X25519() Curve

type PrivateKey struct {
	// Has unexported fields.
}

func (k *PrivateKey) Bytes() []byte
func (k *PrivateKey) Curve() Curve
func (k *PrivateKey) Equal(x crypto.PrivateKey) bool
func (k *PrivateKey) Public() crypto.PublicKey
func (k *PrivateKey) PublicKey() *PublicKey

type PublicKey struct {
	// Has unexported fields.
}

func (k *PublicKey) Bytes() []byte
func (k *PublicKey) Curve() Curve
func (k *PublicKey) Equal(x crypto.PublicKey) bool

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions