// Copyright 2022 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// Package ecdh implements Elliptic Curve Diffie-Hellman over // NIST curves and Curve25519.
package ecdh import ( ) type Curve interface { // GenerateKey generates a random PrivateKey. // // Most applications should use [crypto/rand.Reader] as rand. Note that the // returned key does not depend deterministically on the bytes read from rand, // and may change between calls and/or between versions. 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. 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) // ecdh performs an ECDH exchange and returns the shared secret. It's exposed // as the PrivateKey.ECDH method. // // The private method also allow us to expand the ECDH interface with more // methods in the future without breaking backwards compatibility. ecdh(local *PrivateKey, remote *PublicKey) ([]byte, error) // privateKeyToPublicKey converts a PrivateKey to a PublicKey. It's exposed // as the PrivateKey.PublicKey method. // // This method always succeeds: for X25519, the zero key can't be // constructed due to clamping; for NIST curves, it is rejected by // NewPrivateKey. privateKeyToPublicKey(*PrivateKey) *PublicKey } // PublicKey is an ECDH public key, usually a peer's ECDH share sent over the wire. // // These keys can be parsed with [crypto/x509.ParsePKIXPublicKey] and encoded // with [crypto/x509.MarshalPKIXPublicKey]. For NIST curves, they then need to // be converted with [crypto/ecdsa.PublicKey.ECDH] after parsing. type PublicKey struct { curve Curve publicKey []byte boring *boring.PublicKeyECDH } // Bytes returns a copy of the encoding of the public key. func ( *PublicKey) () []byte { // Copy the public key to a fixed size buffer that can get allocated on the // caller's stack after inlining. var [133]byte return append([:0], .publicKey...) } // Equal returns whether x represents the same public key as k. // // Note that there can be equivalent public keys with different encodings which // would return false from this check but behave the same way as inputs to ECDH. // // This check is performed in constant time as long as the key types and their // curve match. func ( *PublicKey) ( crypto.PublicKey) bool { , := .(*PublicKey) if ! { return false } return .curve == .curve && subtle.ConstantTimeCompare(.publicKey, .publicKey) == 1 } func ( *PublicKey) () Curve { return .curve } // PrivateKey is an ECDH private key, usually kept secret. // // These keys can be parsed with [crypto/x509.ParsePKCS8PrivateKey] and encoded // with [crypto/x509.MarshalPKCS8PrivateKey]. For NIST curves, they then need to // be converted with [crypto/ecdsa.PrivateKey.ECDH] after parsing. type PrivateKey struct { curve Curve privateKey []byte boring *boring.PrivateKeyECDH // publicKey is set under publicKeyOnce, to allow loading private keys with // NewPrivateKey without having to perform a scalar multiplication. publicKey *PublicKey publicKeyOnce sync.Once } // ECDH performs an ECDH exchange and returns the shared secret. The [PrivateKey] // and [PublicKey] must use the same curve. // // 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. The result is never the point at infinity. // // 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. func ( *PrivateKey) ( *PublicKey) ([]byte, error) { if .curve != .curve { return nil, errors.New("crypto/ecdh: private key and public key curves do not match") } return .curve.ecdh(, ) } // Bytes returns a copy of the encoding of the private key. func ( *PrivateKey) () []byte { // Copy the private key to a fixed size buffer that can get allocated on the // caller's stack after inlining. var [66]byte return append([:0], .privateKey...) } // Equal returns whether x represents the same private key as k. // // Note that there can be equivalent private keys with different encodings which // would return false from this check but behave the same way as inputs to [ECDH]. // // This check is performed in constant time as long as the key types and their // curve match. func ( *PrivateKey) ( crypto.PrivateKey) bool { , := .(*PrivateKey) if ! { return false } return .curve == .curve && subtle.ConstantTimeCompare(.privateKey, .privateKey) == 1 } func ( *PrivateKey) () Curve { return .curve } func ( *PrivateKey) () *PublicKey { .publicKeyOnce.Do(func() { if .boring != nil { // Because we already checked in NewPrivateKey that the key is valid, // there should not be any possible errors from BoringCrypto, // so we turn the error into a panic. // (We can't return it anyhow.) , := .boring.PublicKey() if != nil { panic("boringcrypto: " + .Error()) } .publicKey = &PublicKey{ curve: .curve, publicKey: .Bytes(), boring: , } } else { .publicKey = .curve.privateKeyToPublicKey() } }) return .publicKey } // Public implements the implicit interface of all standard library private // keys. See the docs of [crypto.PrivateKey]. func ( *PrivateKey) () crypto.PublicKey { return .PublicKey() }