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