123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367 |
- // 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
- }
- if ctr[2]++; ctr[2] != 0 {
- return
- }
- if ctr[1]++; ctr[1] != 0 {
- return
- }
- if ctr[0]++; ctr[0] != 0 {
- 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(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(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(params, Ke, c[mStart:mEnd])
- return
- }
|