365 lines
		
	
	
		
			9.6 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			365 lines
		
	
	
		
			9.6 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
| // Copyright (c) 2013 Kyle Isom <kyle@tyrfingr.is>
 | |
| // Copyright (c) 2012 The Go Authors. All rights reserved.
 | |
| //
 | |
| // Redistribution and use in source and binary forms, with or without
 | |
| // modification, are permitted provided that the following conditions are
 | |
| // met:
 | |
| //
 | |
| //    * Redistributions of source code must retain the above copyright
 | |
| // notice, this list of conditions and the following disclaimer.
 | |
| //    * Redistributions in binary form must reproduce the above
 | |
| // copyright notice, this list of conditions and the following disclaimer
 | |
| // in the documentation and/or other materials provided with the
 | |
| // distribution.
 | |
| //    * Neither the name of Google Inc. nor the names of its
 | |
| // contributors may be used to endorse or promote products derived from
 | |
| // this software without specific prior written permission.
 | |
| //
 | |
| // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 | |
| // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 | |
| // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 | |
| // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 | |
| // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 | |
| // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 | |
| // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 | |
| // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 | |
| // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 | |
| // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 | |
| // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 | |
| 
 | |
| package ecies
 | |
| 
 | |
| import (
 | |
| 	"crypto/cipher"
 | |
| 	"crypto/ecdsa"
 | |
| 	"crypto/elliptic"
 | |
| 	"crypto/hmac"
 | |
| 	"crypto/subtle"
 | |
| 	"fmt"
 | |
| 	"hash"
 | |
| 	"io"
 | |
| 	"math/big"
 | |
| )
 | |
| 
 | |
| var (
 | |
| 	ErrImport                     = fmt.Errorf("ecies: failed to import key")
 | |
| 	ErrInvalidCurve               = fmt.Errorf("ecies: invalid elliptic curve")
 | |
| 	ErrInvalidParams              = fmt.Errorf("ecies: invalid ECIES parameters")
 | |
| 	ErrInvalidPublicKey           = fmt.Errorf("ecies: invalid public key")
 | |
| 	ErrSharedKeyIsPointAtInfinity = fmt.Errorf("ecies: shared key is point at infinity")
 | |
| 	ErrSharedKeyTooBig            = fmt.Errorf("ecies: shared key params are too big")
 | |
| )
 | |
| 
 | |
| // PublicKey is a representation of an elliptic curve public key.
 | |
| type PublicKey struct {
 | |
| 	X *big.Int
 | |
| 	Y *big.Int
 | |
| 	elliptic.Curve
 | |
| 	Params *ECIESParams
 | |
| }
 | |
| 
 | |
| // Export an ECIES public key as an ECDSA public key.
 | |
| func (pub *PublicKey) ExportECDSA() *ecdsa.PublicKey {
 | |
| 	return &ecdsa.PublicKey{Curve: pub.Curve, X: pub.X, Y: pub.Y}
 | |
| }
 | |
| 
 | |
| // Import an ECDSA public key as an ECIES public key.
 | |
| func ImportECDSAPublic(pub *ecdsa.PublicKey) *PublicKey {
 | |
| 	return &PublicKey{
 | |
| 		X:      pub.X,
 | |
| 		Y:      pub.Y,
 | |
| 		Curve:  pub.Curve,
 | |
| 		Params: ParamsFromCurve(pub.Curve),
 | |
| 	}
 | |
| }
 | |
| 
 | |
| // PrivateKey is a representation of an elliptic curve private key.
 | |
| type PrivateKey struct {
 | |
| 	PublicKey
 | |
| 	D *big.Int
 | |
| }
 | |
| 
 | |
| // Export an ECIES private key as an ECDSA private key.
 | |
| func (prv *PrivateKey) ExportECDSA() *ecdsa.PrivateKey {
 | |
| 	pub := &prv.PublicKey
 | |
| 	pubECDSA := pub.ExportECDSA()
 | |
| 	return &ecdsa.PrivateKey{PublicKey: *pubECDSA, D: prv.D}
 | |
| }
 | |
| 
 | |
| // Import an ECDSA private key as an ECIES private key.
 | |
| func ImportECDSA(prv *ecdsa.PrivateKey) *PrivateKey {
 | |
| 	pub := ImportECDSAPublic(&prv.PublicKey)
 | |
| 	return &PrivateKey{*pub, prv.D}
 | |
| }
 | |
| 
 | |
| // Generate an elliptic curve public / private keypair. If params is nil,
 | |
| // the recommended default parameters for the key will be chosen.
 | |
| func GenerateKey(rand io.Reader, curve elliptic.Curve, params *ECIESParams) (prv *PrivateKey, err error) {
 | |
| 	pb, x, y, err := elliptic.GenerateKey(curve, rand)
 | |
| 	if err != nil {
 | |
| 		return
 | |
| 	}
 | |
| 	prv = new(PrivateKey)
 | |
| 	prv.PublicKey.X = x
 | |
| 	prv.PublicKey.Y = y
 | |
| 	prv.PublicKey.Curve = curve
 | |
| 	prv.D = new(big.Int).SetBytes(pb)
 | |
| 	if params == nil {
 | |
| 		params = ParamsFromCurve(curve)
 | |
| 	}
 | |
| 	prv.PublicKey.Params = params
 | |
| 	return
 | |
| }
 | |
| 
 | |
| // MaxSharedKeyLength returns the maximum length of the shared key the
 | |
| // public key can produce.
 | |
| func MaxSharedKeyLength(pub *PublicKey) int {
 | |
| 	return (pub.Curve.Params().BitSize + 7) / 8
 | |
| }
 | |
| 
 | |
| // ECDH key agreement method used to establish secret keys for encryption.
 | |
| func (prv *PrivateKey) GenerateShared(pub *PublicKey, skLen, macLen int) (sk []byte, err error) {
 | |
| 	if prv.PublicKey.Curve != pub.Curve {
 | |
| 		return nil, ErrInvalidCurve
 | |
| 	}
 | |
| 	if skLen+macLen > MaxSharedKeyLength(pub) {
 | |
| 		return nil, ErrSharedKeyTooBig
 | |
| 	}
 | |
| 
 | |
| 	x, _ := pub.Curve.ScalarMult(pub.X, pub.Y, prv.D.Bytes())
 | |
| 	if x == nil {
 | |
| 		return nil, ErrSharedKeyIsPointAtInfinity
 | |
| 	}
 | |
| 
 | |
| 	sk = make([]byte, skLen+macLen)
 | |
| 	skBytes := x.Bytes()
 | |
| 	copy(sk[len(sk)-len(skBytes):], skBytes)
 | |
| 	return sk, nil
 | |
| }
 | |
| 
 | |
| var (
 | |
| 	ErrKeyDataTooLong = fmt.Errorf("ecies: can't supply requested key data")
 | |
| 	ErrSharedTooLong  = fmt.Errorf("ecies: shared secret is too long")
 | |
| 	ErrInvalidMessage = fmt.Errorf("ecies: invalid message")
 | |
| )
 | |
| 
 | |
| var (
 | |
| 	big2To32   = new(big.Int).Exp(big.NewInt(2), big.NewInt(32), nil)
 | |
| 	big2To32M1 = new(big.Int).Sub(big2To32, big.NewInt(1))
 | |
| )
 | |
| 
 | |
| func incCounter(ctr []byte) {
 | |
| 	if ctr[3]++; ctr[3] != 0 {
 | |
| 		return
 | |
| 	} else if ctr[2]++; ctr[2] != 0 {
 | |
| 		return
 | |
| 	} else if ctr[1]++; ctr[1] != 0 {
 | |
| 		return
 | |
| 	} else if ctr[0]++; ctr[0] != 0 {
 | |
| 		return
 | |
| 	}
 | |
| 	return
 | |
| }
 | |
| 
 | |
| // NIST SP 800-56 Concatenation Key Derivation Function (see section 5.8.1).
 | |
| func concatKDF(hash hash.Hash, z, s1 []byte, kdLen int) (k []byte, err error) {
 | |
| 	if s1 == nil {
 | |
| 		s1 = make([]byte, 0)
 | |
| 	}
 | |
| 
 | |
| 	reps := ((kdLen + 7) * 8) / (hash.BlockSize() * 8)
 | |
| 	if big.NewInt(int64(reps)).Cmp(big2To32M1) > 0 {
 | |
| 		fmt.Println(big2To32M1)
 | |
| 		return nil, ErrKeyDataTooLong
 | |
| 	}
 | |
| 
 | |
| 	counter := []byte{0, 0, 0, 1}
 | |
| 	k = make([]byte, 0)
 | |
| 
 | |
| 	for i := 0; i <= reps; i++ {
 | |
| 		hash.Write(counter)
 | |
| 		hash.Write(z)
 | |
| 		hash.Write(s1)
 | |
| 		k = append(k, hash.Sum(nil)...)
 | |
| 		hash.Reset()
 | |
| 		incCounter(counter)
 | |
| 	}
 | |
| 
 | |
| 	k = k[:kdLen]
 | |
| 	return
 | |
| }
 | |
| 
 | |
| // messageTag computes the MAC of a message (called the tag) as per
 | |
| // SEC 1, 3.5.
 | |
| func messageTag(hash func() hash.Hash, km, msg, shared []byte) []byte {
 | |
| 	mac := hmac.New(hash, km)
 | |
| 	mac.Write(msg)
 | |
| 	mac.Write(shared)
 | |
| 	tag := mac.Sum(nil)
 | |
| 	return tag
 | |
| }
 | |
| 
 | |
| // Generate an initialisation vector for CTR mode.
 | |
| func generateIV(params *ECIESParams, rand io.Reader) (iv []byte, err error) {
 | |
| 	iv = make([]byte, params.BlockSize)
 | |
| 	_, err = io.ReadFull(rand, iv)
 | |
| 	return
 | |
| }
 | |
| 
 | |
| // symEncrypt carries out CTR encryption using the block cipher specified in the
 | |
| // parameters.
 | |
| func symEncrypt(rand io.Reader, params *ECIESParams, key, m []byte) (ct []byte, err error) {
 | |
| 	c, err := params.Cipher(key)
 | |
| 	if err != nil {
 | |
| 		return
 | |
| 	}
 | |
| 
 | |
| 	iv, err := generateIV(params, rand)
 | |
| 	if err != nil {
 | |
| 		return
 | |
| 	}
 | |
| 	ctr := cipher.NewCTR(c, iv)
 | |
| 
 | |
| 	ct = make([]byte, len(m)+params.BlockSize)
 | |
| 	copy(ct, iv)
 | |
| 	ctr.XORKeyStream(ct[params.BlockSize:], m)
 | |
| 	return
 | |
| }
 | |
| 
 | |
| // symDecrypt carries out CTR decryption using the block cipher specified in
 | |
| // the parameters
 | |
| func symDecrypt(rand io.Reader, params *ECIESParams, key, ct []byte) (m []byte, err error) {
 | |
| 	c, err := params.Cipher(key)
 | |
| 	if err != nil {
 | |
| 		return
 | |
| 	}
 | |
| 
 | |
| 	ctr := cipher.NewCTR(c, ct[:params.BlockSize])
 | |
| 
 | |
| 	m = make([]byte, len(ct)-params.BlockSize)
 | |
| 	ctr.XORKeyStream(m, ct[params.BlockSize:])
 | |
| 	return
 | |
| }
 | |
| 
 | |
| // Encrypt encrypts a message using ECIES as specified in SEC 1, 5.1.
 | |
| //
 | |
| // s1 and s2 contain shared information that is not part of the resulting
 | |
| // ciphertext. s1 is fed into key derivation, s2 is fed into the MAC. If the
 | |
| // shared information parameters aren't being used, they should be nil.
 | |
| func Encrypt(rand io.Reader, pub *PublicKey, m, s1, s2 []byte) (ct []byte, err error) {
 | |
| 	params := pub.Params
 | |
| 	if params == nil {
 | |
| 		if params = ParamsFromCurve(pub.Curve); params == nil {
 | |
| 			err = ErrUnsupportedECIESParameters
 | |
| 			return
 | |
| 		}
 | |
| 	}
 | |
| 	R, err := GenerateKey(rand, pub.Curve, params)
 | |
| 	if err != nil {
 | |
| 		return
 | |
| 	}
 | |
| 
 | |
| 	hash := params.Hash()
 | |
| 	z, err := R.GenerateShared(pub, params.KeyLen, params.KeyLen)
 | |
| 	if err != nil {
 | |
| 		return
 | |
| 	}
 | |
| 	K, err := concatKDF(hash, z, s1, params.KeyLen+params.KeyLen)
 | |
| 	if err != nil {
 | |
| 		return
 | |
| 	}
 | |
| 	Ke := K[:params.KeyLen]
 | |
| 	Km := K[params.KeyLen:]
 | |
| 	hash.Write(Km)
 | |
| 	Km = hash.Sum(nil)
 | |
| 	hash.Reset()
 | |
| 
 | |
| 	em, err := symEncrypt(rand, params, Ke, m)
 | |
| 	if err != nil || len(em) <= params.BlockSize {
 | |
| 		return
 | |
| 	}
 | |
| 
 | |
| 	d := messageTag(params.Hash, Km, em, s2)
 | |
| 
 | |
| 	Rb := elliptic.Marshal(pub.Curve, R.PublicKey.X, R.PublicKey.Y)
 | |
| 	ct = make([]byte, len(Rb)+len(em)+len(d))
 | |
| 	copy(ct, Rb)
 | |
| 	copy(ct[len(Rb):], em)
 | |
| 	copy(ct[len(Rb)+len(em):], d)
 | |
| 	return
 | |
| }
 | |
| 
 | |
| // Decrypt decrypts an ECIES ciphertext.
 | |
| func (prv *PrivateKey) Decrypt(rand io.Reader, c, s1, s2 []byte) (m []byte, err error) {
 | |
| 	if len(c) == 0 {
 | |
| 		return nil, ErrInvalidMessage
 | |
| 	}
 | |
| 	params := prv.PublicKey.Params
 | |
| 	if params == nil {
 | |
| 		if params = ParamsFromCurve(prv.PublicKey.Curve); params == nil {
 | |
| 			err = ErrUnsupportedECIESParameters
 | |
| 			return
 | |
| 		}
 | |
| 	}
 | |
| 	hash := params.Hash()
 | |
| 
 | |
| 	var (
 | |
| 		rLen   int
 | |
| 		hLen   int = hash.Size()
 | |
| 		mStart int
 | |
| 		mEnd   int
 | |
| 	)
 | |
| 
 | |
| 	switch c[0] {
 | |
| 	case 2, 3, 4:
 | |
| 		rLen = ((prv.PublicKey.Curve.Params().BitSize + 7) / 4)
 | |
| 		if len(c) < (rLen + hLen + 1) {
 | |
| 			err = ErrInvalidMessage
 | |
| 			return
 | |
| 		}
 | |
| 	default:
 | |
| 		err = ErrInvalidPublicKey
 | |
| 		return
 | |
| 	}
 | |
| 
 | |
| 	mStart = rLen
 | |
| 	mEnd = len(c) - hLen
 | |
| 
 | |
| 	R := new(PublicKey)
 | |
| 	R.Curve = prv.PublicKey.Curve
 | |
| 	R.X, R.Y = elliptic.Unmarshal(R.Curve, c[:rLen])
 | |
| 	if R.X == nil {
 | |
| 		err = ErrInvalidPublicKey
 | |
| 		return
 | |
| 	}
 | |
| 	if !R.Curve.IsOnCurve(R.X, R.Y) {
 | |
| 		err = ErrInvalidCurve
 | |
| 		return
 | |
| 	}
 | |
| 
 | |
| 	z, err := prv.GenerateShared(R, params.KeyLen, params.KeyLen)
 | |
| 	if err != nil {
 | |
| 		return
 | |
| 	}
 | |
| 
 | |
| 	K, err := concatKDF(hash, z, s1, params.KeyLen+params.KeyLen)
 | |
| 	if err != nil {
 | |
| 		return
 | |
| 	}
 | |
| 
 | |
| 	Ke := K[:params.KeyLen]
 | |
| 	Km := K[params.KeyLen:]
 | |
| 	hash.Write(Km)
 | |
| 	Km = hash.Sum(nil)
 | |
| 	hash.Reset()
 | |
| 
 | |
| 	d := messageTag(params.Hash, Km, c[mStart:mEnd], s2)
 | |
| 	if subtle.ConstantTimeCompare(c[mEnd:], d) != 1 {
 | |
| 		err = ErrInvalidMessage
 | |
| 		return
 | |
| 	}
 | |
| 
 | |
| 	m, err = symDecrypt(rand, params, Ke, c[mStart:mEnd])
 | |
| 	return
 | |
| }
 |