1// Copyright 2022 The Go Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style
3// license that can be found in the LICENSE file.
4
5// Package ecdh implements Elliptic Curve Diffie-Hellman over
6// NIST curves and Curve25519.
7package ecdh
8
9import (
10	"crypto"
11	"crypto/internal/boring"
12	"crypto/subtle"
13	"errors"
14	"io"
15	"sync"
16)
17
18type Curve interface {
19	// GenerateKey generates a random PrivateKey.
20	//
21	// Most applications should use [crypto/rand.Reader] as rand. Note that the
22	// returned key does not depend deterministically on the bytes read from rand,
23	// and may change between calls and/or between versions.
24	GenerateKey(rand io.Reader) (*PrivateKey, error)
25
26	// NewPrivateKey checks that key is valid and returns a PrivateKey.
27	//
28	// For NIST curves, this follows SEC 1, Version 2.0, Section 2.3.6, which
29	// amounts to decoding the bytes as a fixed length big endian integer and
30	// checking that the result is lower than the order of the curve. The zero
31	// private key is also rejected, as the encoding of the corresponding public
32	// key would be irregular.
33	//
34	// For X25519, this only checks the scalar length.
35	NewPrivateKey(key []byte) (*PrivateKey, error)
36
37	// NewPublicKey checks that key is valid and returns a PublicKey.
38	//
39	// For NIST curves, this decodes an uncompressed point according to SEC 1,
40	// Version 2.0, Section 2.3.4. Compressed encodings and the point at
41	// infinity are rejected.
42	//
43	// For X25519, this only checks the u-coordinate length. Adversarially
44	// selected public keys can cause ECDH to return an error.
45	NewPublicKey(key []byte) (*PublicKey, error)
46
47	// ecdh performs an ECDH exchange and returns the shared secret. It's exposed
48	// as the PrivateKey.ECDH method.
49	//
50	// The private method also allow us to expand the ECDH interface with more
51	// methods in the future without breaking backwards compatibility.
52	ecdh(local *PrivateKey, remote *PublicKey) ([]byte, error)
53
54	// privateKeyToPublicKey converts a PrivateKey to a PublicKey. It's exposed
55	// as the PrivateKey.PublicKey method.
56	//
57	// This method always succeeds: for X25519, the zero key can't be
58	// constructed due to clamping; for NIST curves, it is rejected by
59	// NewPrivateKey.
60	privateKeyToPublicKey(*PrivateKey) *PublicKey
61}
62
63// PublicKey is an ECDH public key, usually a peer's ECDH share sent over the wire.
64//
65// These keys can be parsed with [crypto/x509.ParsePKIXPublicKey] and encoded
66// with [crypto/x509.MarshalPKIXPublicKey]. For NIST curves, they then need to
67// be converted with [crypto/ecdsa.PublicKey.ECDH] after parsing.
68type PublicKey struct {
69	curve     Curve
70	publicKey []byte
71	boring    *boring.PublicKeyECDH
72}
73
74// Bytes returns a copy of the encoding of the public key.
75func (k *PublicKey) Bytes() []byte {
76	// Copy the public key to a fixed size buffer that can get allocated on the
77	// caller's stack after inlining.
78	var buf [133]byte
79	return append(buf[:0], k.publicKey...)
80}
81
82// Equal returns whether x represents the same public key as k.
83//
84// Note that there can be equivalent public keys with different encodings which
85// would return false from this check but behave the same way as inputs to ECDH.
86//
87// This check is performed in constant time as long as the key types and their
88// curve match.
89func (k *PublicKey) Equal(x crypto.PublicKey) bool {
90	xx, ok := x.(*PublicKey)
91	if !ok {
92		return false
93	}
94	return k.curve == xx.curve &&
95		subtle.ConstantTimeCompare(k.publicKey, xx.publicKey) == 1
96}
97
98func (k *PublicKey) Curve() Curve {
99	return k.curve
100}
101
102// PrivateKey is an ECDH private key, usually kept secret.
103//
104// These keys can be parsed with [crypto/x509.ParsePKCS8PrivateKey] and encoded
105// with [crypto/x509.MarshalPKCS8PrivateKey]. For NIST curves, they then need to
106// be converted with [crypto/ecdsa.PrivateKey.ECDH] after parsing.
107type PrivateKey struct {
108	curve      Curve
109	privateKey []byte
110	boring     *boring.PrivateKeyECDH
111	// publicKey is set under publicKeyOnce, to allow loading private keys with
112	// NewPrivateKey without having to perform a scalar multiplication.
113	publicKey     *PublicKey
114	publicKeyOnce sync.Once
115}
116
117// ECDH performs an ECDH exchange and returns the shared secret. The [PrivateKey]
118// and [PublicKey] must use the same curve.
119//
120// For NIST curves, this performs ECDH as specified in SEC 1, Version 2.0,
121// Section 3.3.1, and returns the x-coordinate encoded according to SEC 1,
122// Version 2.0, Section 2.3.5. The result is never the point at infinity.
123//
124// For [X25519], this performs ECDH as specified in RFC 7748, Section 6.1. If
125// the result is the all-zero value, ECDH returns an error.
126func (k *PrivateKey) ECDH(remote *PublicKey) ([]byte, error) {
127	if k.curve != remote.curve {
128		return nil, errors.New("crypto/ecdh: private key and public key curves do not match")
129	}
130	return k.curve.ecdh(k, remote)
131}
132
133// Bytes returns a copy of the encoding of the private key.
134func (k *PrivateKey) Bytes() []byte {
135	// Copy the private key to a fixed size buffer that can get allocated on the
136	// caller's stack after inlining.
137	var buf [66]byte
138	return append(buf[:0], k.privateKey...)
139}
140
141// Equal returns whether x represents the same private key as k.
142//
143// Note that there can be equivalent private keys with different encodings which
144// would return false from this check but behave the same way as inputs to [ECDH].
145//
146// This check is performed in constant time as long as the key types and their
147// curve match.
148func (k *PrivateKey) Equal(x crypto.PrivateKey) bool {
149	xx, ok := x.(*PrivateKey)
150	if !ok {
151		return false
152	}
153	return k.curve == xx.curve &&
154		subtle.ConstantTimeCompare(k.privateKey, xx.privateKey) == 1
155}
156
157func (k *PrivateKey) Curve() Curve {
158	return k.curve
159}
160
161func (k *PrivateKey) PublicKey() *PublicKey {
162	k.publicKeyOnce.Do(func() {
163		if k.boring != nil {
164			// Because we already checked in NewPrivateKey that the key is valid,
165			// there should not be any possible errors from BoringCrypto,
166			// so we turn the error into a panic.
167			// (We can't return it anyhow.)
168			kpub, err := k.boring.PublicKey()
169			if err != nil {
170				panic("boringcrypto: " + err.Error())
171			}
172			k.publicKey = &PublicKey{
173				curve:     k.curve,
174				publicKey: kpub.Bytes(),
175				boring:    kpub,
176			}
177		} else {
178			k.publicKey = k.curve.privateKeyToPublicKey(k)
179		}
180	})
181	return k.publicKey
182}
183
184// Public implements the implicit interface of all standard library private
185// keys. See the docs of [crypto.PrivateKey].
186func (k *PrivateKey) Public() crypto.PublicKey {
187	return k.PublicKey()
188}
189