github.com/emmansun/gmsm@v0.29.1/ecdh/ecdh.go (about)

     1  // Package ecdh implements Elliptic Curve Diffie-Hellman / SM2-MQV over
     2  // SM2 curve.
     3  package ecdh
     4  
     5  import (
     6  	"crypto"
     7  	"crypto/subtle"
     8  	"hash"
     9  	"io"
    10  	"sync"
    11  
    12  	"github.com/emmansun/gmsm/sm3"
    13  )
    14  
    15  type Curve interface {
    16  	// GenerateKey generates a new PrivateKey from rand.
    17  	GenerateKey(rand io.Reader) (*PrivateKey, error)
    18  
    19  	// NewPrivateKey checks that key is valid and returns a PrivateKey.
    20  	//
    21  	// For NIST curves, this follows SEC 1, Version 2.0, Section 2.3.6, which
    22  	// amounts to decoding the bytes as a fixed length big endian integer and
    23  	// checking that the result is lower than the order of the curve. The zero
    24  	// private key is also rejected, as the encoding of the corresponding public
    25  	// key would be irregular.
    26  	//
    27  	// For X25519, this only checks the scalar length. Adversarially selected
    28  	// private keys can cause ECDH to return an error.
    29  	NewPrivateKey(key []byte) (*PrivateKey, error)
    30  
    31  	// NewPublicKey checks that key is valid and returns a PublicKey.
    32  	//
    33  	// For NIST curves, this decodes an uncompressed point according to SEC 1,
    34  	// Version 2.0, Section 2.3.4. Compressed encodings and the point at
    35  	// infinity are rejected.
    36  	//
    37  	// For X25519, this only checks the u-coordinate length. Adversarially
    38  	// selected public keys can cause ECDH to return an error.
    39  	NewPublicKey(key []byte) (*PublicKey, error)
    40  
    41  	// ecdh performs a ECDH exchange and returns the shared secret. It's exposed
    42  	// as the PrivateKey.ECDH method.
    43  	//
    44  	// The private method also allow us to expand the ECDH interface with more
    45  	// methods in the future without breaking backwards compatibility.
    46  	ecdh(local *PrivateKey, remote *PublicKey) ([]byte, error)
    47  
    48  	// sm2mqv performs a SM2 specific style ECMQV exchange and return the shared secret.
    49  	sm2mqv(sLocal, eLocal *PrivateKey, sRemote, eRemote *PublicKey) (*PublicKey, error)
    50  
    51  	// sm2za ZA = H256(ENTLA || IDA || a || b || xG || yG || xA || yA).
    52  	// Compliance with GB/T 32918.2-2016 5.5
    53  	sm2za(md hash.Hash, pub *PublicKey, uid []byte) ([]byte, error)
    54  
    55  	// privateKeyToPublicKey converts a PrivateKey to a PublicKey. It's exposed
    56  	// as the PrivateKey.PublicKey method.
    57  	//
    58  	// This method always succeeds: for X25519, it might output the all-zeroes
    59  	// value (unlike the ECDH method); for NIST curves, it would only fail for
    60  	// the zero private key, which is rejected by NewPrivateKey.
    61  	//
    62  	// The private method also allow us to expand the ECDH interface with more
    63  	// methods in the future without breaking backwards compatibility.
    64  	privateKeyToPublicKey(*PrivateKey) *PublicKey
    65  }
    66  
    67  // PublicKey is an ECDH public key, usually a peer's ECDH share sent over the wire.
    68  //
    69  // These keys can be parsed with [smx509.ParsePKIXPublicKey] and encoded
    70  // with [smx509.MarshalPKIXPublicKey]. For SM2 curve, it then needs to
    71  // be converted with [sm2.PublicKeyToECDH] after parsing.
    72  type PublicKey struct {
    73  	curve     Curve
    74  	publicKey []byte
    75  }
    76  
    77  // Bytes returns a copy of the encoding of the public key.
    78  func (k *PublicKey) Bytes() []byte {
    79  	// Copy the public key to a fixed size buffer that can get allocated on the
    80  	// caller's stack after inlining.
    81  	var buf [133]byte
    82  	return append(buf[:0], k.publicKey...)
    83  }
    84  
    85  // Equal returns whether x represents the same public key as k.
    86  //
    87  // Note that there can be equivalent public keys with different encodings which
    88  // would return false from this check but behave the same way as inputs to ECDH.
    89  //
    90  // This check is performed in constant time as long as the key types and their
    91  // curve match.
    92  func (k *PublicKey) Equal(x crypto.PublicKey) bool {
    93  	xx, ok := x.(*PublicKey)
    94  	if !ok {
    95  		return false
    96  	}
    97  	return k.curve == xx.curve &&
    98  		subtle.ConstantTimeCompare(k.publicKey, xx.publicKey) == 1
    99  }
   100  
   101  func (k *PublicKey) Curve() Curve {
   102  	return k.curve
   103  }
   104  
   105  // SM2ZA ZA = H256(ENTLA || IDA || a || b || xG || yG || xA || yA).
   106  // Compliance with GB/T 32918.2-2016 5.5
   107  func (k *PublicKey) SM2ZA(md hash.Hash, uid []byte) ([]byte, error) {
   108  	return k.curve.sm2za(md, k, uid)
   109  }
   110  
   111  // SM2SharedKey performs SM2 key derivation to generate shared keying data, the uv was generated by SM2MQV.
   112  func (uv *PublicKey) SM2SharedKey(isResponder bool, kenLen int, sPub, sRemote *PublicKey, uid []byte, remoteUID []byte) ([]byte, error) {
   113  	var buffer [128]byte
   114  	copy(buffer[:], uv.publicKey[1:])
   115  	peerZ, err := sRemote.SM2ZA(sm3.New(), remoteUID)
   116  	if err != nil {
   117  		return nil, err
   118  	}
   119  	z, err := sPub.SM2ZA(sm3.New(), uid)
   120  	if err != nil {
   121  		return nil, err
   122  	}
   123  	if isResponder {
   124  		copy(buffer[64:], peerZ)
   125  		copy(buffer[96:], z)
   126  	} else {
   127  		copy(buffer[64:], z)
   128  		copy(buffer[96:], peerZ)
   129  	}
   130  
   131  	return sm3.Kdf(buffer[:], kenLen), nil
   132  }
   133  
   134  // PrivateKey is an ECDH private key, usually kept secret.
   135  //
   136  // These keys can be parsed with [smx509.ParsePKCS8PrivateKey] and encoded
   137  // with [smx509.MarshalPKCS8PrivateKey]. For SM2 curve, it then needs to
   138  // be converted with [sm2.PrivateKey.ECDH] after parsing.
   139  type PrivateKey struct {
   140  	curve      Curve
   141  	privateKey []byte
   142  	// publicKey is set under publicKeyOnce, to allow loading private keys with
   143  	// NewPrivateKey without having to perform a scalar multiplication.
   144  	publicKey     *PublicKey
   145  	publicKeyOnce sync.Once
   146  }
   147  
   148  // ECDH performs a ECDH exchange and returns the shared secret.
   149  //
   150  // For NIST curves, this performs ECDH as specified in SEC 1, Version 2.0,
   151  // Section 3.3.1, and returns the x-coordinate encoded according to SEC 1,
   152  // Version 2.0, Section 2.3.5. The result is never the point at infinity.
   153  //
   154  // For X25519, this performs ECDH as specified in RFC 7748, Section 6.1. If
   155  // the result is the all-zero value, ECDH returns an error.
   156  func (k *PrivateKey) ECDH(remote *PublicKey) ([]byte, error) {
   157  	return k.curve.ecdh(k, remote)
   158  }
   159  
   160  // SM2MQV performs a SM2 specific style ECMQV exchange and return the shared secret.
   161  func (k *PrivateKey) SM2MQV(eLocal *PrivateKey, sRemote, eRemote *PublicKey) (*PublicKey, error) {
   162  	return k.curve.sm2mqv(k, eLocal, sRemote, eRemote)
   163  }
   164  
   165  // Bytes returns a copy of the encoding of the private key.
   166  func (k *PrivateKey) Bytes() []byte {
   167  	// Copy the private key to a fixed size buffer that can get allocated on the
   168  	// caller's stack after inlining.
   169  	var buf [66]byte
   170  	return append(buf[:0], k.privateKey...)
   171  }
   172  
   173  // Equal returns whether x represents the same private key as k.
   174  //
   175  // Note that there can be equivalent private keys with different encodings which
   176  // would return false from this check but behave the same way as inputs to ECDH.
   177  //
   178  // This check is performed in constant time as long as the key types and their
   179  // curve match.
   180  func (k *PrivateKey) Equal(x crypto.PrivateKey) bool {
   181  	xx, ok := x.(*PrivateKey)
   182  	if !ok {
   183  		return false
   184  	}
   185  	return k.curve == xx.curve &&
   186  		subtle.ConstantTimeCompare(k.privateKey, xx.privateKey) == 1
   187  }
   188  
   189  func (k *PrivateKey) Curve() Curve {
   190  	return k.curve
   191  }
   192  
   193  func (k *PrivateKey) PublicKey() *PublicKey {
   194  	k.publicKeyOnce.Do(func() {
   195  		k.publicKey = k.curve.privateKeyToPublicKey(k)
   196  	})
   197  	return k.publicKey
   198  }
   199  
   200  // Public implements the implicit interface of all standard library private
   201  // keys. See the docs of crypto.PrivateKey.
   202  func (k *PrivateKey) Public() crypto.PublicKey {
   203  	return k.PublicKey()
   204  }