ecdh.mx raw

   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.
   7  package ecdh
   8  
   9  import (
  10  	"crypto"
  11  	"crypto/internal/boring"
  12  	"crypto/internal/fips140/ecdh"
  13  	"crypto/subtle"
  14  	"errors"
  15  	"io"
  16  )
  17  
  18  type 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  
  55  // PublicKey is an ECDH public key, usually a peer's ECDH share sent over the wire.
  56  //
  57  // These keys can be parsed with [crypto/x509.ParsePKIXPublicKey] and encoded
  58  // with [crypto/x509.MarshalPKIXPublicKey]. For NIST curves, they then need to
  59  // be converted with [crypto/ecdsa.PublicKey.ECDH] after parsing.
  60  type PublicKey struct {
  61  	curve     Curve
  62  	publicKey []byte
  63  	boring    *boring.PublicKeyECDH
  64  	fips      *ecdh.PublicKey
  65  }
  66  
  67  // Bytes returns a copy of the encoding of the public key.
  68  func (k *PublicKey) Bytes() []byte {
  69  	// Copy the public key to a fixed size buffer that can get allocated on the
  70  	// caller's stack after inlining.
  71  	var buf [133]byte
  72  	return append(buf[:0], k.publicKey...)
  73  }
  74  
  75  // Equal returns whether x represents the same public key as k.
  76  //
  77  // Note that there can be equivalent public keys with different encodings which
  78  // would return false from this check but behave the same way as inputs to ECDH.
  79  //
  80  // This check is performed in constant time as long as the key types and their
  81  // curve match.
  82  func (k *PublicKey) Equal(x crypto.PublicKey) bool {
  83  	xx, ok := x.(*PublicKey)
  84  	if !ok {
  85  		return false
  86  	}
  87  	return k.curve == xx.curve &&
  88  		subtle.ConstantTimeCompare(k.publicKey, xx.publicKey) == 1
  89  }
  90  
  91  func (k *PublicKey) Curve() Curve {
  92  	return k.curve
  93  }
  94  
  95  // PrivateKey is an ECDH private key, usually kept secret.
  96  //
  97  // These keys can be parsed with [crypto/x509.ParsePKCS8PrivateKey] and encoded
  98  // with [crypto/x509.MarshalPKCS8PrivateKey]. For NIST curves, they then need to
  99  // be converted with [crypto/ecdsa.PrivateKey.ECDH] after parsing.
 100  type PrivateKey struct {
 101  	curve      Curve
 102  	privateKey []byte
 103  	publicKey  *PublicKey
 104  	boring     *boring.PrivateKeyECDH
 105  	fips       *ecdh.PrivateKey
 106  }
 107  
 108  // ECDH performs an ECDH exchange and returns the shared secret. The [PrivateKey]
 109  // and [PublicKey] must use the same curve.
 110  //
 111  // For NIST curves, this performs ECDH as specified in SEC 1, Version 2.0,
 112  // Section 3.3.1, and returns the x-coordinate encoded according to SEC 1,
 113  // Version 2.0, Section 2.3.5. The result is never the point at infinity.
 114  // This is also known as the Shared Secret Computation of the Ephemeral Unified
 115  // Model scheme specified in NIST SP 800-56A Rev. 3, Section 6.1.2.2.
 116  //
 117  // For [X25519], this performs ECDH as specified in RFC 7748, Section 6.1. If
 118  // the result is the all-zero value, ECDH returns an error.
 119  func (k *PrivateKey) ECDH(remote *PublicKey) ([]byte, error) {
 120  	if k.curve != remote.curve {
 121  		return nil, errors.New("crypto/ecdh: private key and public key curves do not match")
 122  	}
 123  	return k.curve.ecdh(k, remote)
 124  }
 125  
 126  // Bytes returns a copy of the encoding of the private key.
 127  func (k *PrivateKey) Bytes() []byte {
 128  	// Copy the private key to a fixed size buffer that can get allocated on the
 129  	// caller's stack after inlining.
 130  	var buf [66]byte
 131  	return append(buf[:0], k.privateKey...)
 132  }
 133  
 134  // Equal returns whether x represents the same private key as k.
 135  //
 136  // Note that there can be equivalent private keys with different encodings which
 137  // would return false from this check but behave the same way as inputs to [ECDH].
 138  //
 139  // This check is performed in constant time as long as the key types and their
 140  // curve match.
 141  func (k *PrivateKey) Equal(x crypto.PrivateKey) bool {
 142  	xx, ok := x.(*PrivateKey)
 143  	if !ok {
 144  		return false
 145  	}
 146  	return k.curve == xx.curve &&
 147  		subtle.ConstantTimeCompare(k.privateKey, xx.privateKey) == 1
 148  }
 149  
 150  func (k *PrivateKey) Curve() Curve {
 151  	return k.curve
 152  }
 153  
 154  func (k *PrivateKey) PublicKey() *PublicKey {
 155  	return k.publicKey
 156  }
 157  
 158  // Public implements the implicit interface of all standard library private
 159  // keys. See the docs of [crypto.PrivateKey].
 160  func (k *PrivateKey) Public() crypto.PublicKey {
 161  	return k.PublicKey()
 162  }
 163