github.com/twelsh-aw/go/src@v0.0.0-20230516233729-a56fe86a7c81/crypto/ecdh/ecdh.go (about)

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