mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-08-20 10:52:25 +00:00
crypto/ecies: add check on keyLen in ECIESParams
This avoids any possibility of counter overflow in concatKDF. Also removed all the naked returns in Encrypt and Decrypt.
This commit is contained in:
parent
2215cdfef4
commit
c8d0688920
3 changed files with 44 additions and 228 deletions
|
|
@ -1,198 +1,3 @@
|
||||||
// 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"
|
|
||||||
"encoding/binary"
|
|
||||||
"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 (
|
|
||||||
ErrSharedTooLong = fmt.Errorf("ecies: shared secret is too long")
|
|
||||||
ErrInvalidMessage = fmt.Errorf("ecies: invalid message")
|
|
||||||
)
|
|
||||||
|
|
||||||
// NIST SP 800-56 Concatenation Key Derivation Function (see section 5.8.1).
|
|
||||||
func concatKDF(hash hash.Hash, z, s1 []byte, kdLen int) []byte {
|
|
||||||
counterBytes := make([]byte, 4)
|
|
||||||
k := make([]byte, 0, roundup(kdLen, hash.Size()))
|
|
||||||
for counter := uint32(1); len(k) < kdLen; counter++ {
|
|
||||||
binary.BigEndian.PutUint32(counterBytes, counter)
|
|
||||||
hash.Reset()
|
|
||||||
hash.Write(counterBytes)
|
|
||||||
hash.Write(z)
|
|
||||||
hash.Write(s1)
|
|
||||||
k = hash.Sum(k)
|
|
||||||
}
|
|
||||||
return k[:kdLen]
|
|
||||||
}
|
|
||||||
|
|
||||||
// roundup rounds size up to the next multiple of blocksize.
|
|
||||||
func roundup(size, blocksize int) int {
|
|
||||||
return size + blocksize - (size % blocksize)
|
|
||||||
}
|
|
||||||
|
|
||||||
// deriveKeys creates the encryption and MAC keys using concatKDF.
|
|
||||||
func deriveKeys(hash hash.Hash, z, s1 []byte, keyLen int) (Ke, Km []byte) {
|
|
||||||
K := concatKDF(hash, z, s1, 2*keyLen)
|
|
||||||
Ke = K[:keyLen]
|
|
||||||
Km = K[keyLen:]
|
|
||||||
hash.Reset()
|
|
||||||
hash.Write(Km)
|
|
||||||
Km = hash.Sum(Km[:0])
|
|
||||||
return Ke, Km
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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) {
|
func symEncrypt(rand io.Reader, params *ECIESParams, key, m []byte) (ct []byte, err error) {
|
||||||
c, err := params.Cipher(key)
|
c, err := params.Cipher(key)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
@ -232,28 +37,27 @@ func symDecrypt(params *ECIESParams, key, ct []byte) (m []byte, err error) {
|
||||||
// ciphertext. s1 is fed into key derivation, s2 is fed into the MAC. If the
|
// 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.
|
// 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) {
|
func Encrypt(rand io.Reader, pub *PublicKey, m, s1, s2 []byte) (ct []byte, err error) {
|
||||||
params := pub.Params
|
params, err := pubkeyParams(pub)
|
||||||
if params == nil {
|
if err != nil {
|
||||||
if params = ParamsFromCurve(pub.Curve); params == nil {
|
return nil, err
|
||||||
err = ErrUnsupportedECIESParameters
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
R, err := GenerateKey(rand, pub.Curve, params)
|
R, err := GenerateKey(rand, pub.Curve, params)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
z, err := R.GenerateShared(pub, params.KeyLen, params.KeyLen)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
hash := params.Hash()
|
hash := params.Hash()
|
||||||
z, err := R.GenerateShared(pub, params.KeyLen, params.KeyLen)
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
Ke, Km := deriveKeys(hash, z, s1, params.KeyLen)
|
Ke, Km := deriveKeys(hash, z, s1, params.KeyLen)
|
||||||
|
|
||||||
em, err := symEncrypt(rand, params, Ke, m)
|
em, err := symEncrypt(rand, params, Ke, m)
|
||||||
if err != nil || len(em) <= params.BlockSize {
|
if err != nil || len(em) <= params.BlockSize {
|
||||||
return
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
d := messageTag(params.Hash, Km, em, s2)
|
d := messageTag(params.Hash, Km, em, s2)
|
||||||
|
|
@ -263,7 +67,7 @@ func Encrypt(rand io.Reader, pub *PublicKey, m, s1, s2 []byte) (ct []byte, err e
|
||||||
copy(ct, Rb)
|
copy(ct, Rb)
|
||||||
copy(ct[len(Rb):], em)
|
copy(ct[len(Rb):], em)
|
||||||
copy(ct[len(Rb)+len(em):], d)
|
copy(ct[len(Rb)+len(em):], d)
|
||||||
return
|
return ct, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Decrypt decrypts an ECIES ciphertext.
|
// Decrypt decrypts an ECIES ciphertext.
|
||||||
|
|
@ -271,13 +75,11 @@ func (prv *PrivateKey) Decrypt(c, s1, s2 []byte) (m []byte, err error) {
|
||||||
if len(c) == 0 {
|
if len(c) == 0 {
|
||||||
return nil, ErrInvalidMessage
|
return nil, ErrInvalidMessage
|
||||||
}
|
}
|
||||||
params := prv.PublicKey.Params
|
params, err := pubkeyParams(&prv.PublicKey)
|
||||||
if params == nil {
|
if err != nil {
|
||||||
if params = ParamsFromCurve(prv.PublicKey.Curve); params == nil {
|
return nil, err
|
||||||
err = ErrUnsupportedECIESParameters
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
hash := params.Hash()
|
hash := params.Hash()
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
|
@ -291,12 +93,10 @@ func (prv *PrivateKey) Decrypt(c, s1, s2 []byte) (m []byte, err error) {
|
||||||
case 2, 3, 4:
|
case 2, 3, 4:
|
||||||
rLen = (prv.PublicKey.Curve.Params().BitSize + 7) / 4
|
rLen = (prv.PublicKey.Curve.Params().BitSize + 7) / 4
|
||||||
if len(c) < (rLen + hLen + 1) {
|
if len(c) < (rLen + hLen + 1) {
|
||||||
err = ErrInvalidMessage
|
return nil, ErrInvalidMessage
|
||||||
return
|
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
err = ErrInvalidPublicKey
|
return nil, ErrInvalidPublicKey
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
||||||
mStart = rLen
|
mStart = rLen
|
||||||
|
|
@ -306,12 +106,10 @@ func (prv *PrivateKey) Decrypt(c, s1, s2 []byte) (m []byte, err error) {
|
||||||
R.Curve = prv.PublicKey.Curve
|
R.Curve = prv.PublicKey.Curve
|
||||||
R.X, R.Y = elliptic.Unmarshal(R.Curve, c[:rLen])
|
R.X, R.Y = elliptic.Unmarshal(R.Curve, c[:rLen])
|
||||||
if R.X == nil {
|
if R.X == nil {
|
||||||
err = ErrInvalidPublicKey
|
return nil, ErrInvalidPublicKey
|
||||||
return
|
|
||||||
}
|
}
|
||||||
if !R.Curve.IsOnCurve(R.X, R.Y) {
|
if !R.Curve.IsOnCurve(R.X, R.Y) {
|
||||||
err = ErrInvalidCurve
|
return nil, ErrInvalidCurve
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
||||||
z, err := prv.GenerateShared(R, params.KeyLen, params.KeyLen)
|
z, err := prv.GenerateShared(R, params.KeyLen, params.KeyLen)
|
||||||
|
|
@ -322,9 +120,8 @@ func (prv *PrivateKey) Decrypt(c, s1, s2 []byte) (m []byte, err error) {
|
||||||
|
|
||||||
d := messageTag(params.Hash, Km, c[mStart:mEnd], s2)
|
d := messageTag(params.Hash, Km, c[mStart:mEnd], s2)
|
||||||
if subtle.ConstantTimeCompare(c[mEnd:], d) != 1 {
|
if subtle.ConstantTimeCompare(c[mEnd:], d) != 1 {
|
||||||
err = ErrInvalidMessage
|
return nil, ErrInvalidMessage
|
||||||
return
|
|
||||||
}
|
}
|
||||||
m, err = symDecrypt(params, Ke, c[mStart:mEnd])
|
|
||||||
return
|
return symDecrypt(params, Ke, c[mStart:mEnd])
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -299,8 +299,8 @@ func TestParamSelection(t *testing.T) {
|
||||||
|
|
||||||
func testParamSelection(t *testing.T, c testCase) {
|
func testParamSelection(t *testing.T, c testCase) {
|
||||||
params := ParamsFromCurve(c.Curve)
|
params := ParamsFromCurve(c.Curve)
|
||||||
if params == nil && c.Expected != nil {
|
if params == nil {
|
||||||
t.Fatalf("%s (%s)\n", ErrInvalidParams.Error(), c.Name)
|
t.Fatal("ParamsFromCurve returned nil")
|
||||||
} else if params != nil && !cmpParams(params, c.Expected) {
|
} else if params != nil && !cmpParams(params, c.Expected) {
|
||||||
t.Fatalf("ecies: parameters should be invalid (%s)\n", c.Name)
|
t.Fatalf("ecies: parameters should be invalid (%s)\n", c.Name)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -49,8 +49,14 @@ var (
|
||||||
DefaultCurve = ethcrypto.S256()
|
DefaultCurve = ethcrypto.S256()
|
||||||
ErrUnsupportedECDHAlgorithm = fmt.Errorf("ecies: unsupported ECDH algorithm")
|
ErrUnsupportedECDHAlgorithm = fmt.Errorf("ecies: unsupported ECDH algorithm")
|
||||||
ErrUnsupportedECIESParameters = fmt.Errorf("ecies: unsupported ECIES parameters")
|
ErrUnsupportedECIESParameters = fmt.Errorf("ecies: unsupported ECIES parameters")
|
||||||
|
ErrInvalidKeyLen = fmt.Errorf("ecies: invalid key size (> 256) in ECIESParams")
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// Maximem key size is limited to prevent overflow of the counter
|
||||||
|
// in ConcatKDF. While the theoretical limit is much higher, no known
|
||||||
|
// cipher uses keys larger than 512 bytes.
|
||||||
|
const maxKeyLen = 512
|
||||||
|
|
||||||
type ECIESParams struct {
|
type ECIESParams struct {
|
||||||
Hash func() hash.Hash // hash function
|
Hash func() hash.Hash // hash function
|
||||||
hashAlgo crypto.Hash
|
hashAlgo crypto.Hash
|
||||||
|
|
@ -115,3 +121,16 @@ func AddParamsForCurve(curve elliptic.Curve, params *ECIESParams) {
|
||||||
func ParamsFromCurve(curve elliptic.Curve) (params *ECIESParams) {
|
func ParamsFromCurve(curve elliptic.Curve) (params *ECIESParams) {
|
||||||
return paramsFromCurve[curve]
|
return paramsFromCurve[curve]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func pubkeyParams(key *PublicKey) (*ECIESParams, error) {
|
||||||
|
params := key.Params
|
||||||
|
if params == nil {
|
||||||
|
if params = ParamsFromCurve(key.Curve); params == nil {
|
||||||
|
return nil, ErrUnsupportedECIESParameters
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if params.KeyLen > maxKeyLen {
|
||||||
|
return nil, ErrInvalidKeyLen
|
||||||
|
}
|
||||||
|
return params, nil
|
||||||
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue