github.com/mtsmfm/go/src@v0.0.0-20221020090648-44bdcb9f8fde/crypto/ecdh/nist.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
     6  
     7  import (
     8  	"crypto/internal/nistec"
     9  	"crypto/internal/randutil"
    10  	"encoding/binary"
    11  	"errors"
    12  	"io"
    13  	"math/bits"
    14  )
    15  
    16  type nistCurve[Point nistPoint[Point]] struct {
    17  	name        string
    18  	newPoint    func() Point
    19  	scalarOrder []byte
    20  }
    21  
    22  // nistPoint is a generic constraint for the nistec Point types.
    23  type nistPoint[T any] interface {
    24  	Bytes() []byte
    25  	BytesX() ([]byte, error)
    26  	SetBytes([]byte) (T, error)
    27  	ScalarMult(T, []byte) (T, error)
    28  	ScalarBaseMult([]byte) (T, error)
    29  }
    30  
    31  func (c *nistCurve[Point]) String() string {
    32  	return c.name
    33  }
    34  
    35  var errInvalidPrivateKey = errors.New("crypto/ecdh: invalid private key")
    36  
    37  func (c *nistCurve[Point]) GenerateKey(rand io.Reader) (*PrivateKey, error) {
    38  	key := make([]byte, len(c.scalarOrder))
    39  	randutil.MaybeReadByte(rand)
    40  	for {
    41  		if _, err := io.ReadFull(rand, key); err != nil {
    42  			return nil, err
    43  		}
    44  
    45  		// Mask off any excess bits if the size of the underlying field is not a
    46  		// whole number of bytes, which is only the case for P-521. We use a
    47  		// pointer to the scalarOrder field because comparing generic and
    48  		// instantiated types is not supported.
    49  		if &c.scalarOrder[0] == &p521Order[0] {
    50  			key[0] &= 0b0000_0001
    51  		}
    52  
    53  		// In tests, rand will return all zeros and NewPrivateKey will reject
    54  		// the zero key as it generates the identity as a public key. This also
    55  		// makes this function consistent with crypto/elliptic.GenerateKey.
    56  		key[1] ^= 0x42
    57  
    58  		k, err := c.NewPrivateKey(key)
    59  		if err == errInvalidPrivateKey {
    60  			continue
    61  		}
    62  		return k, err
    63  	}
    64  }
    65  
    66  func (c *nistCurve[Point]) NewPrivateKey(key []byte) (*PrivateKey, error) {
    67  	if len(key) != len(c.scalarOrder) {
    68  		return nil, errors.New("crypto/ecdh: invalid private key size")
    69  	}
    70  	if isZero(key) || !isLess(key, c.scalarOrder) {
    71  		return nil, errInvalidPrivateKey
    72  	}
    73  	return &PrivateKey{
    74  		curve:      c,
    75  		privateKey: append([]byte{}, key...),
    76  	}, nil
    77  }
    78  
    79  func (c *nistCurve[Point]) privateKeyToPublicKey(key *PrivateKey) *PublicKey {
    80  	if key.curve != c {
    81  		panic("crypto/ecdh: internal error: converting the wrong key type")
    82  	}
    83  	p, err := c.newPoint().ScalarBaseMult(key.privateKey)
    84  	if err != nil {
    85  		// This is unreachable because the only error condition of
    86  		// ScalarBaseMult is if the input is not the right size.
    87  		panic("crypto/ecdh: internal error: nistec ScalarBaseMult failed for a fixed-size input")
    88  	}
    89  	publicKey := p.Bytes()
    90  	if len(publicKey) == 1 {
    91  		// The encoding of the identity is a single 0x00 byte. This is
    92  		// unreachable because the only scalar that generates the identity is
    93  		// zero, which is rejected by NewPrivateKey.
    94  		panic("crypto/ecdh: internal error: nistec ScalarBaseMult returned the identity")
    95  	}
    96  	return &PublicKey{
    97  		curve:     key.curve,
    98  		publicKey: publicKey,
    99  	}
   100  }
   101  
   102  // isZero returns whether a is all zeroes in constant time.
   103  func isZero(a []byte) bool {
   104  	var acc byte
   105  	for _, b := range a {
   106  		acc |= b
   107  	}
   108  	return acc == 0
   109  }
   110  
   111  // isLess returns whether a < b, where a and b are big-endian buffers of the
   112  // same length and shorter than 72 bytes.
   113  func isLess(a, b []byte) bool {
   114  	if len(a) != len(b) {
   115  		panic("crypto/ecdh: internal error: mismatched isLess inputs")
   116  	}
   117  
   118  	// Copy the values into a fixed-size preallocated little-endian buffer.
   119  	// 72 bytes is enough for every scalar in this package, and having a fixed
   120  	// size lets us avoid heap allocations.
   121  	if len(a) > 72 {
   122  		panic("crypto/ecdh: internal error: isLess input too large")
   123  	}
   124  	bufA, bufB := make([]byte, 72), make([]byte, 72)
   125  	for i := range a {
   126  		bufA[i], bufB[i] = a[len(a)-i-1], b[len(b)-i-1]
   127  	}
   128  
   129  	// Perform a subtraction with borrow.
   130  	var borrow uint64
   131  	for i := 0; i < len(bufA); i += 8 {
   132  		limbA, limbB := binary.LittleEndian.Uint64(bufA[i:]), binary.LittleEndian.Uint64(bufB[i:])
   133  		_, borrow = bits.Sub64(limbA, limbB, borrow)
   134  	}
   135  
   136  	// If there is a borrow at the end of the operation, then a < b.
   137  	return borrow == 1
   138  }
   139  
   140  func (c *nistCurve[Point]) NewPublicKey(key []byte) (*PublicKey, error) {
   141  	// Reject the point at infinity and compressed encodings.
   142  	if len(key) == 0 || key[0] != 4 {
   143  		return nil, errors.New("crypto/ecdh: invalid public key")
   144  	}
   145  	// SetBytes also checks that the point is on the curve.
   146  	if _, err := c.newPoint().SetBytes(key); err != nil {
   147  		return nil, err
   148  	}
   149  
   150  	return &PublicKey{
   151  		curve:     c,
   152  		publicKey: append([]byte{}, key...),
   153  	}, nil
   154  }
   155  
   156  func (c *nistCurve[Point]) ECDH(local *PrivateKey, remote *PublicKey) ([]byte, error) {
   157  	p, err := c.newPoint().SetBytes(remote.publicKey)
   158  	if err != nil {
   159  		return nil, err
   160  	}
   161  	if _, err := p.ScalarMult(p, local.privateKey); err != nil {
   162  		return nil, err
   163  	}
   164  	// BytesX will return an error if p is the point at infinity.
   165  	return p.BytesX()
   166  }
   167  
   168  // P256 returns a Curve which implements NIST P-256 (FIPS 186-3, section D.2.3),
   169  // also known as secp256r1 or prime256v1.
   170  //
   171  // Multiple invocations of this function will return the same value, so it can
   172  // be used for equality checks and switch statements.
   173  func P256() Curve { return p256 }
   174  
   175  var p256 = &nistCurve[*nistec.P256Point]{
   176  	name:        "P-256",
   177  	newPoint:    nistec.NewP256Point,
   178  	scalarOrder: p256Order,
   179  }
   180  
   181  var p256Order = []byte{0xff, 0xff, 0xff, 0xff, 0x0, 0x0, 0x0, 0x0, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xbc, 0xe6, 0xfa, 0xad, 0xa7, 0x17, 0x9e, 0x84, 0xf3, 0xb9, 0xca, 0xc2, 0xfc, 0x63, 0x25, 0x51}
   182  
   183  // P384 returns a Curve which implements NIST P-384 (FIPS 186-3, section D.2.4),
   184  // also known as secp384r1.
   185  //
   186  // Multiple invocations of this function will return the same value, so it can
   187  // be used for equality checks and switch statements.
   188  func P384() Curve { return p384 }
   189  
   190  var p384 = &nistCurve[*nistec.P384Point]{
   191  	name:        "P-384",
   192  	newPoint:    nistec.NewP384Point,
   193  	scalarOrder: p384Order,
   194  }
   195  
   196  var p384Order = []byte{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc7, 0x63, 0x4d, 0x81, 0xf4, 0x37, 0x2d, 0xdf, 0x58, 0x1a, 0xd, 0xb2, 0x48, 0xb0, 0xa7, 0x7a, 0xec, 0xec, 0x19, 0x6a, 0xcc, 0xc5, 0x29, 0x73}
   197  
   198  // P521 returns a Curve which implements NIST P-521 (FIPS 186-3, section D.2.5),
   199  // also known as secp521r1.
   200  //
   201  // Multiple invocations of this function will return the same value, so it can
   202  // be used for equality checks and switch statements.
   203  func P521() Curve { return p521 }
   204  
   205  var p521 = &nistCurve[*nistec.P521Point]{
   206  	name:        "P-521",
   207  	newPoint:    nistec.NewP521Point,
   208  	scalarOrder: p521Order,
   209  }
   210  
   211  var p521Order = []byte{0x1, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfa, 0x51, 0x86, 0x87, 0x83, 0xbf, 0x2f, 0x96, 0x6b, 0x7f, 0xcc, 0x1, 0x48, 0xf7, 0x9, 0xa5, 0xd0, 0x3b, 0xb5, 0xc9, 0xb8, 0x89, 0x9c, 0x47, 0xae, 0xbb, 0x6f, 0xb7, 0x1e, 0x91, 0x38, 0x64, 0x9}