github.1485827954.workers.dev/ethereum/go-ethereum@v1.14.3/crypto/ecies/ecies.go (about)

     1  // Copyright (c) 2013 Kyle Isom <kyle@tyrfingr.is>
     2  // Copyright (c) 2012 The Go Authors. All rights reserved.
     3  //
     4  // Redistribution and use in source and binary forms, with or without
     5  // modification, are permitted provided that the following conditions are
     6  // met:
     7  //
     8  //    * Redistributions of source code must retain the above copyright
     9  // notice, this list of conditions and the following disclaimer.
    10  //    * Redistributions in binary form must reproduce the above
    11  // copyright notice, this list of conditions and the following disclaimer
    12  // in the documentation and/or other materials provided with the
    13  // distribution.
    14  //    * Neither the name of Google Inc. nor the names of its
    15  // contributors may be used to endorse or promote products derived from
    16  // this software without specific prior written permission.
    17  //
    18  // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
    19  // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
    20  // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
    21  // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
    22  // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
    23  // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
    24  // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
    25  // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
    26  // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
    27  // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
    28  // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
    29  
    30  package ecies
    31  
    32  import (
    33  	"crypto/cipher"
    34  	"crypto/ecdsa"
    35  	"crypto/elliptic"
    36  	"crypto/hmac"
    37  	"crypto/subtle"
    38  	"encoding/binary"
    39  	"errors"
    40  	"hash"
    41  	"io"
    42  	"math/big"
    43  
    44  	"github.com/ethereum/go-ethereum/crypto"
    45  )
    46  
    47  var (
    48  	ErrImport                     = errors.New("ecies: failed to import key")
    49  	ErrInvalidCurve               = errors.New("ecies: invalid elliptic curve")
    50  	ErrInvalidPublicKey           = errors.New("ecies: invalid public key")
    51  	ErrSharedKeyIsPointAtInfinity = errors.New("ecies: shared key is point at infinity")
    52  	ErrSharedKeyTooBig            = errors.New("ecies: shared key params are too big")
    53  )
    54  
    55  // PublicKey is a representation of an elliptic curve public key.
    56  type PublicKey struct {
    57  	X *big.Int
    58  	Y *big.Int
    59  	elliptic.Curve
    60  	Params *ECIESParams
    61  }
    62  
    63  // Export an ECIES public key as an ECDSA public key.
    64  func (pub *PublicKey) ExportECDSA() *ecdsa.PublicKey {
    65  	return &ecdsa.PublicKey{Curve: pub.Curve, X: pub.X, Y: pub.Y}
    66  }
    67  
    68  // Import an ECDSA public key as an ECIES public key.
    69  func ImportECDSAPublic(pub *ecdsa.PublicKey) *PublicKey {
    70  	return &PublicKey{
    71  		X:      pub.X,
    72  		Y:      pub.Y,
    73  		Curve:  pub.Curve,
    74  		Params: ParamsFromCurve(pub.Curve),
    75  	}
    76  }
    77  
    78  // PrivateKey is a representation of an elliptic curve private key.
    79  type PrivateKey struct {
    80  	PublicKey
    81  	D *big.Int
    82  }
    83  
    84  // Export an ECIES private key as an ECDSA private key.
    85  func (prv *PrivateKey) ExportECDSA() *ecdsa.PrivateKey {
    86  	pub := &prv.PublicKey
    87  	pubECDSA := pub.ExportECDSA()
    88  	return &ecdsa.PrivateKey{PublicKey: *pubECDSA, D: prv.D}
    89  }
    90  
    91  // Import an ECDSA private key as an ECIES private key.
    92  func ImportECDSA(prv *ecdsa.PrivateKey) *PrivateKey {
    93  	pub := ImportECDSAPublic(&prv.PublicKey)
    94  	return &PrivateKey{*pub, prv.D}
    95  }
    96  
    97  // Generate an elliptic curve public / private keypair. If params is nil,
    98  // the recommended default parameters for the key will be chosen.
    99  func GenerateKey(rand io.Reader, curve elliptic.Curve, params *ECIESParams) (prv *PrivateKey, err error) {
   100  	sk, err := ecdsa.GenerateKey(curve, rand)
   101  	if err != nil {
   102  		return
   103  	}
   104  	prv = new(PrivateKey)
   105  	prv.PublicKey.X = sk.X
   106  	prv.PublicKey.Y = sk.Y
   107  	prv.PublicKey.Curve = curve
   108  	prv.D = new(big.Int).Set(sk.D)
   109  	if params == nil {
   110  		params = ParamsFromCurve(curve)
   111  	}
   112  	prv.PublicKey.Params = params
   113  	return
   114  }
   115  
   116  // MaxSharedKeyLength returns the maximum length of the shared key the
   117  // public key can produce.
   118  func MaxSharedKeyLength(pub *PublicKey) int {
   119  	return (pub.Curve.Params().BitSize + 7) / 8
   120  }
   121  
   122  // ECDH key agreement method used to establish secret keys for encryption.
   123  func (prv *PrivateKey) GenerateShared(pub *PublicKey, skLen, macLen int) (sk []byte, err error) {
   124  	if prv.PublicKey.Curve != pub.Curve {
   125  		return nil, ErrInvalidCurve
   126  	}
   127  	if skLen+macLen > MaxSharedKeyLength(pub) {
   128  		return nil, ErrSharedKeyTooBig
   129  	}
   130  
   131  	x, _ := pub.Curve.ScalarMult(pub.X, pub.Y, prv.D.Bytes())
   132  	if x == nil {
   133  		return nil, ErrSharedKeyIsPointAtInfinity
   134  	}
   135  
   136  	sk = make([]byte, skLen+macLen)
   137  	skBytes := x.Bytes()
   138  	copy(sk[len(sk)-len(skBytes):], skBytes)
   139  	return sk, nil
   140  }
   141  
   142  var (
   143  	ErrSharedTooLong  = errors.New("ecies: shared secret is too long")
   144  	ErrInvalidMessage = errors.New("ecies: invalid message")
   145  )
   146  
   147  // NIST SP 800-56 Concatenation Key Derivation Function (see section 5.8.1).
   148  func concatKDF(hash hash.Hash, z, s1 []byte, kdLen int) []byte {
   149  	counterBytes := make([]byte, 4)
   150  	k := make([]byte, 0, roundup(kdLen, hash.Size()))
   151  	for counter := uint32(1); len(k) < kdLen; counter++ {
   152  		binary.BigEndian.PutUint32(counterBytes, counter)
   153  		hash.Reset()
   154  		hash.Write(counterBytes)
   155  		hash.Write(z)
   156  		hash.Write(s1)
   157  		k = hash.Sum(k)
   158  	}
   159  	return k[:kdLen]
   160  }
   161  
   162  // roundup rounds size up to the next multiple of blocksize.
   163  func roundup(size, blocksize int) int {
   164  	return size + blocksize - (size % blocksize)
   165  }
   166  
   167  // deriveKeys creates the encryption and MAC keys using concatKDF.
   168  func deriveKeys(hash hash.Hash, z, s1 []byte, keyLen int) (Ke, Km []byte) {
   169  	K := concatKDF(hash, z, s1, 2*keyLen)
   170  	Ke = K[:keyLen]
   171  	Km = K[keyLen:]
   172  	hash.Reset()
   173  	hash.Write(Km)
   174  	Km = hash.Sum(Km[:0])
   175  	return Ke, Km
   176  }
   177  
   178  // messageTag computes the MAC of a message (called the tag) as per
   179  // SEC 1, 3.5.
   180  func messageTag(hash func() hash.Hash, km, msg, shared []byte) []byte {
   181  	mac := hmac.New(hash, km)
   182  	mac.Write(msg)
   183  	mac.Write(shared)
   184  	tag := mac.Sum(nil)
   185  	return tag
   186  }
   187  
   188  // Generate an initialisation vector for CTR mode.
   189  func generateIV(params *ECIESParams, rand io.Reader) (iv []byte, err error) {
   190  	iv = make([]byte, params.BlockSize)
   191  	_, err = io.ReadFull(rand, iv)
   192  	return
   193  }
   194  
   195  // symEncrypt carries out CTR encryption using the block cipher specified in the
   196  func symEncrypt(rand io.Reader, params *ECIESParams, key, m []byte) (ct []byte, err error) {
   197  	c, err := params.Cipher(key)
   198  	if err != nil {
   199  		return
   200  	}
   201  
   202  	iv, err := generateIV(params, rand)
   203  	if err != nil {
   204  		return
   205  	}
   206  	ctr := cipher.NewCTR(c, iv)
   207  
   208  	ct = make([]byte, len(m)+params.BlockSize)
   209  	copy(ct, iv)
   210  	ctr.XORKeyStream(ct[params.BlockSize:], m)
   211  	return
   212  }
   213  
   214  // symDecrypt carries out CTR decryption using the block cipher specified in
   215  // the parameters
   216  func symDecrypt(params *ECIESParams, key, ct []byte) (m []byte, err error) {
   217  	c, err := params.Cipher(key)
   218  	if err != nil {
   219  		return
   220  	}
   221  
   222  	ctr := cipher.NewCTR(c, ct[:params.BlockSize])
   223  
   224  	m = make([]byte, len(ct)-params.BlockSize)
   225  	ctr.XORKeyStream(m, ct[params.BlockSize:])
   226  	return
   227  }
   228  
   229  // Encrypt encrypts a message using ECIES as specified in SEC 1, 5.1.
   230  //
   231  // s1 and s2 contain shared information that is not part of the resulting
   232  // ciphertext. s1 is fed into key derivation, s2 is fed into the MAC. If the
   233  // shared information parameters aren't being used, they should be nil.
   234  func Encrypt(rand io.Reader, pub *PublicKey, m, s1, s2 []byte) (ct []byte, err error) {
   235  	params, err := pubkeyParams(pub)
   236  	if err != nil {
   237  		return nil, err
   238  	}
   239  
   240  	R, err := GenerateKey(rand, pub.Curve, params)
   241  	if err != nil {
   242  		return nil, err
   243  	}
   244  
   245  	z, err := R.GenerateShared(pub, params.KeyLen, params.KeyLen)
   246  	if err != nil {
   247  		return nil, err
   248  	}
   249  
   250  	hash := params.Hash()
   251  	Ke, Km := deriveKeys(hash, z, s1, params.KeyLen)
   252  
   253  	em, err := symEncrypt(rand, params, Ke, m)
   254  	if err != nil || len(em) <= params.BlockSize {
   255  		return nil, err
   256  	}
   257  
   258  	d := messageTag(params.Hash, Km, em, s2)
   259  
   260  	if curve, ok := pub.Curve.(crypto.EllipticCurve); ok {
   261  		Rb := curve.Marshal(R.PublicKey.X, R.PublicKey.Y)
   262  		ct = make([]byte, len(Rb)+len(em)+len(d))
   263  		copy(ct, Rb)
   264  		copy(ct[len(Rb):], em)
   265  		copy(ct[len(Rb)+len(em):], d)
   266  		return ct, nil
   267  	}
   268  	return nil, ErrInvalidCurve
   269  }
   270  
   271  // Decrypt decrypts an ECIES ciphertext.
   272  func (prv *PrivateKey) Decrypt(c, s1, s2 []byte) (m []byte, err error) {
   273  	if len(c) == 0 {
   274  		return nil, ErrInvalidMessage
   275  	}
   276  	params, err := pubkeyParams(&prv.PublicKey)
   277  	if err != nil {
   278  		return nil, err
   279  	}
   280  
   281  	hash := params.Hash()
   282  
   283  	var (
   284  		rLen   int
   285  		hLen   int = hash.Size()
   286  		mStart int
   287  		mEnd   int
   288  	)
   289  
   290  	switch c[0] {
   291  	case 2, 3, 4:
   292  		rLen = (prv.PublicKey.Curve.Params().BitSize + 7) / 4
   293  		if len(c) < (rLen + hLen + 1) {
   294  			return nil, ErrInvalidMessage
   295  		}
   296  	default:
   297  		return nil, ErrInvalidPublicKey
   298  	}
   299  
   300  	mStart = rLen
   301  	mEnd = len(c) - hLen
   302  
   303  	R := new(PublicKey)
   304  	R.Curve = prv.PublicKey.Curve
   305  
   306  	if curve, ok := R.Curve.(crypto.EllipticCurve); ok {
   307  		R.X, R.Y = curve.Unmarshal(c[:rLen])
   308  		if R.X == nil {
   309  			return nil, ErrInvalidPublicKey
   310  		}
   311  
   312  		z, err := prv.GenerateShared(R, params.KeyLen, params.KeyLen)
   313  		if err != nil {
   314  			return nil, err
   315  		}
   316  		Ke, Km := deriveKeys(hash, z, s1, params.KeyLen)
   317  
   318  		d := messageTag(params.Hash, Km, c[mStart:mEnd], s2)
   319  		if subtle.ConstantTimeCompare(c[mEnd:], d) != 1 {
   320  			return nil, ErrInvalidMessage
   321  		}
   322  		return symDecrypt(params, Ke, c[mStart:mEnd])
   323  	}
   324  	return nil, ErrInvalidCurve
   325  }