diff --git a/go.mod b/go.mod index 4de4596..02600cc 100644 --- a/go.mod +++ b/go.mod @@ -3,7 +3,10 @@ module github.com/foundriesio/fioconfig go 1.13 require ( + github.com/ThalesIgnite/crypto11 v1.2.1 github.com/ethereum/go-ethereum v1.9.11 github.com/pelletier/go-toml v1.8.0 github.com/urfave/cli/v2 v2.2.0 ) + +replace github.com/ThalesIgnite/crypto11 => github.com/doanac/crypto11 v1.2.2-0.20200715151421-f3d2e17ac497 diff --git a/go.sum b/go.sum index 4378381..dd3e64d 100644 --- a/go.sum +++ b/go.sum @@ -15,6 +15,7 @@ github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03 github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= github.com/OneOfOne/xxhash v1.2.5/go.mod h1:eZbhyaAYD41SGSSsnmcpxVoRiQ/MPUTjUdIIOT9Um7Q= github.com/StackExchange/wmi v0.0.0-20180116203802-5d049714c4a6/go.mod h1:3eOhrUMpNV+6aFIbp5/iudMxNCF27Vw2OZgy4xEx0Fg= +github.com/ThalesIgnite/crypto11 v1.2.1/go.mod h1:vmlYtalkn8uCp3eStRZ0r7Sslmf1jAtL8De0PIyqPks= github.com/VictoriaMetrics/fastcache v1.5.3/go.mod h1:+jv9Ckb+za/P1ZRg/sulP5Ni1v49daAVERr0H3CuscE= github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= @@ -37,6 +38,8 @@ github.com/deckarep/golang-set v0.0.0-20180603214616-504e848d77ea/go.mod h1:93vs github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no= github.com/dlclark/regexp2 v1.2.0/go.mod h1:2pZnwuY/m+8K6iRw6wQdMtk+rH5tNGR1i55kozfMjCc= +github.com/doanac/crypto11 v1.2.2-0.20200715151421-f3d2e17ac497 h1:iyvvQeLbHqdu5L6x8OJLZ7rZb4tvoBs0h5liRkvIh6M= +github.com/doanac/crypto11 v1.2.2-0.20200715151421-f3d2e17ac497/go.mod h1:vmlYtalkn8uCp3eStRZ0r7Sslmf1jAtL8De0PIyqPks= github.com/docker/docker v1.4.2-0.20180625184442-8e610b2b55bf/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= github.com/dop251/goja v0.0.0-20200106141417-aaec0e7bde29/go.mod h1:Mw6PkjjMXWbTj+nnj4s3QPXq1jaT0s5pC0iFD4+BOAA= github.com/edsrzf/mmap-go v0.0.0-20160512033002-935e0e8a636c/go.mod h1:YO35OhQPt3KJa3ryjFM5Bs14WD66h8eGKpfaBNrHW5M= @@ -79,6 +82,8 @@ github.com/mattn/go-isatty v0.0.5-0.20180830101745-3fb116b82035/go.mod h1:M+lRXT github.com/mattn/go-runewidth v0.0.3/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= github.com/mattn/go-runewidth v0.0.4/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= +github.com/miekg/pkcs11 v1.0.3-0.20190429190417-a667d056470f h1:eVB9ELsoq5ouItQBr5Tj334bhPJG/MX+m7rTchmzVUQ= +github.com/miekg/pkcs11 v1.0.3-0.20190429190417-a667d056470f/go.mod h1:XsNlhZGX73bx86s2hdc/FuaLm2CPZJemRLMA+WTFxgs= github.com/naoina/go-stringutil v0.1.0/go.mod h1:XJ2SJL9jCtBh+P9q5btrd/Ylo8XwT/h1USek5+NqSA0= github.com/naoina/toml v0.1.2-0.20170918210437-9fafd6967416/go.mod h1:NBIhNtsFMo3G2szEBne+bO4gS192HuIYRqfvOWb4i1E= github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U= @@ -93,6 +98,7 @@ github.com/pelletier/go-toml v1.8.0 h1:Keo9qb7iRJs2voHvunFtuuYFsbWeOBh8/P9v/kVMF github.com/pelletier/go-toml v1.8.0/go.mod h1:D6yutnOGMveHEPV7VQOuvI/gXY61bv+9bAOTRnLElKs= github.com/peterh/liner v1.1.1-0.20190123174540-a2c9a5303de7/go.mod h1:CRroGNssyjTd/qIG2FyxByd2S8JEAZXBl4qUrZf8GS0= github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= @@ -117,6 +123,8 @@ github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXf github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/syndtr/goleveldb v1.0.1-0.20190923125748-758128399b1d/go.mod h1:9OrXJhf154huy1nPWmuSrkgjPUtUNhA+Zmy+6AESzuA= +github.com/thales-e-security/pool v0.0.1 h1:1eJJNN2K/mAzwfr546brAiQVa3UaRC0gGENsHM8veS8= +github.com/thales-e-security/pool v0.0.1/go.mod h1:qtpMm2+thHtqhLzTwgDBj/OuNnMpupY8mv0Phz0gjhU= github.com/tyler-smith/go-bip39 v1.0.1-0.20181017060643-dbb3b84ba2ef/go.mod h1:sJ5fKU0s6JVwZjjcUEX2zFOnvq0ASQ2K9Zr6cf67kNs= github.com/urfave/cli v1.22.1 h1:+mkCCcOFKPnCmVYVcURKps1Xe+3zP90gSYGNfRkjoIY= github.com/urfave/cli v1.22.1/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= diff --git a/internal/app.go b/internal/app.go index 8dd624e..9617500 100644 --- a/internal/app.go +++ b/internal/app.go @@ -15,6 +15,7 @@ import ( "path/filepath" "time" + "github.com/ThalesIgnite/crypto11" toml "github.com/pelletier/go-toml" ) @@ -56,10 +57,72 @@ func tomlAssertVal(tree *toml.Tree, key string, allowed []string) string { return val } -func createClient(sota *toml.Tree) (*http.Client, CryptoHandler) { - _ = tomlAssertVal(sota, "tls.ca_source", []string{"file"}) - _ = tomlAssertVal(sota, "tls.pkey_source", []string{"file"}) - _ = tomlAssertVal(sota, "tls.cert_source", []string{"file"}) +// sota.toml has slot id's as "01". We need to turn that into []byte{1} +func idToBytes(id string) []byte { + bytes := []byte(id) + start := -1 + for idx, char := range bytes { + bytes[idx] = char - byte('0') + if bytes[idx] != 0 && start == -1 { + start = idx + } + } + //strip off leading 0's + return bytes[start:] +} + +func createClientPkcs11(sota *toml.Tree) (*http.Client, CryptoHandler) { + module := tomlGet(sota, "p11.module") + pin := tomlGet(sota, "p11.pass") + pkeyId := tomlGet(sota, "p11.tls_pkey_id") + certId := tomlGet(sota, "p11.tls_clientcert_id") + caFile := tomlGet(sota, "import.tls_cacert_path") + + cfg := crypto11.Config{ + Path: module, + TokenLabel: "aktualizr", + Pin: pin, + } + + ctx, err := crypto11.Configure(&cfg) + if err != nil { + log.Fatal(err) + } + + privKey, err := ctx.FindKeyPair(idToBytes(pkeyId), nil) + if err != nil { + log.Fatal(err) + } + cert, err := ctx.FindCertificate(idToBytes(certId), nil, nil) + if err != nil { + log.Fatal(err) + } + if cert == nil || privKey == nil { + log.Fatal("Unable to load pkcs11 client cert and/or private key") + } + + caCert, err := ioutil.ReadFile(caFile) + if err != nil { + log.Fatal(err) + } + caCertPool := x509.NewCertPool() + caCertPool.AppendCertsFromPEM(caCert) + + tlsConfig := &tls.Config{ + Certificates: []tls.Certificate{ + tls.Certificate{ + Certificate: [][]byte{cert.Raw}, + PrivateKey: privKey, + }, + }, + RootCAs: caCertPool, + } + transport := &http.Transport{TLSClientConfig: tlsConfig} + client := &http.Client{Timeout: time.Second * 30, Transport: transport} + return client, NewEciesPkcs11Handler(ctx, privKey) +} + +func createClientLocal(sota *toml.Tree) (*http.Client, CryptoHandler) { certFile := tomlGet(sota, "import.tls_clientcert_path") keyFile := tomlGet(sota, "import.tls_pkey_path") caFile := tomlGet(sota, "import.tls_cacert_path") @@ -83,12 +146,22 @@ func createClient(sota *toml.Tree) (*http.Client, CryptoHandler) { transport := &http.Transport{TLSClientConfig: tlsConfig} client := &http.Client{Timeout: time.Second * 30, Transport: transport} - if handler := NewEciesHandler(cert.PrivateKey); handler != nil { + if handler := NewEciesLocalHandler(cert.PrivateKey); handler != nil { return client, handler } panic("Unsupported private key") } +func createClient(sota *toml.Tree) (*http.Client, CryptoHandler) { + _ = tomlAssertVal(sota, "tls.ca_source", []string{"file"}) + source := tomlAssertVal(sota, "tls.pkey_source", []string{"file", "pkcs11"}) + _ = tomlAssertVal(sota, "tls.cert_source", []string{source}) + if source == "file" { + return createClientLocal(sota) + } + return createClientPkcs11(sota) +} + func NewApp(sota_config, secrets_dir string, testing bool) (*App, error) { sota, err := toml.LoadFile(filepath.Join(sota_config, "sota.toml")) if err != nil { diff --git a/internal/ecies.go b/internal/ecies.go index 3b89ec7..b5d7791 100644 --- a/internal/ecies.go +++ b/internal/ecies.go @@ -6,16 +6,16 @@ import ( "encoding/base64" "fmt" - "github.com/ethereum/go-ethereum/crypto/ecies" + "github.com/ThalesIgnite/crypto11" ) type EciesCrypto struct { - PrivKey *ecies.PrivateKey + PrivKey PrivateKey } -func NewEciesHandler(privKey crypto.PrivateKey) CryptoHandler { +func NewEciesLocalHandler(privKey crypto.PrivateKey) CryptoHandler { if ec, ok := privKey.(*ecdsa.PrivateKey); ok { - return &EciesCrypto{ecies.ImportECDSA(ec)} + return &EciesCrypto{ImportECDSA(ec)} } return nil } @@ -25,9 +25,13 @@ func (ec *EciesCrypto) Decrypt(value string) ([]byte, error) { if err != nil { return nil, fmt.Errorf("Unable to base64 decode: %v", err) } - decrypted, err := ec.PrivKey.Decrypt(data, nil, nil) + decrypted, err := EciesDecrypt(ec.PrivKey, data, nil, nil) if err != nil { return nil, fmt.Errorf("Unable to ECIES decrypt %v", err) } return decrypted, nil } + +func NewEciesPkcs11Handler(ctx *crypto11.Context, privKey crypto11.Signer) CryptoHandler { + return &EciesCrypto{ImportPcks11(ctx, privKey)} +} diff --git a/internal/ecies_ethereum.go b/internal/ecies_ethereum.go new file mode 100644 index 0000000..faf8d24 --- /dev/null +++ b/internal/ecies_ethereum.go @@ -0,0 +1,253 @@ +// Copyright (c) 2013 Kyle Isom +// 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. + +// This is forked from: +// https://github.com/ethereum/go-ethereum/blob/02cea2330d6b4822b43a7fbaeacc12ddc8e8b1db/crypto/ecies/ecies.go +// to work with both local private keys and pcks11 based keys + +package internal + +import ( + "crypto" + "crypto/cipher" + "crypto/ecdsa" + "crypto/elliptic" + "crypto/hmac" + "crypto/subtle" + "encoding/binary" + "fmt" + "hash" + + "github.com/ThalesIgnite/crypto11" +) + +var ( + ErrInvalidCurve = fmt.Errorf("ecies: invalid elliptic curve") + 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") +) + +type PrivateKey interface { + GenerateShared(pub *ecdsa.PublicKey, skLen, macLen int) (sk []byte, err error) + Public() *ecdsa.PublicKey +} + +// PrivateKeyLocal is a representation of an elliptic curve private key. +type PrivateKeyLocal struct { + *ecdsa.PrivateKey +} + +// Import an ECDSA private key as an ECIES private key. +func ImportECDSA(prv *ecdsa.PrivateKey) *PrivateKeyLocal { + return &PrivateKeyLocal{prv} +} + +type PrivateKeyPkcs11 struct { + ctx *crypto11.Context + signer crypto11.Signer +} + +func ImportPcks11(ctx *crypto11.Context, privKey crypto.PrivateKey) *PrivateKeyPkcs11 { + return &PrivateKeyPkcs11{ctx, privKey.(crypto11.Signer)} +} + +func (prv *PrivateKeyPkcs11) GenerateShared(pub *ecdsa.PublicKey, skLen, macLen int) (sk []byte, err error) { + return prv.ctx.ECDH1Derive(prv.signer, pub) +} +func (prv *PrivateKeyPkcs11) Public() *ecdsa.PublicKey { + pub := prv.signer.Public() + return pub.(*ecdsa.PublicKey) +} + +// MaxSharedKeyLength returns the maximum length of the shared key the +// public key can produce. +func MaxSharedKeyLength(pub *ecdsa.PublicKey) int { + return (pub.Curve.Params().BitSize + 7) / 8 +} + +func (prv *PrivateKeyLocal) Public() *ecdsa.PublicKey { + return &prv.PublicKey +} + +// ECDH key agreement method used to establish secret keys for encryption. +func (prv *PrivateKeyLocal) GenerateShared(pub *ecdsa.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() + if _, err := hash.Write(counterBytes); err != nil { + return nil + } + if _, err := hash.Write(z); err != nil { + return nil + } + if _, err := hash.Write(s1); err != nil { + return nil + } + 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) + if K == nil { + return nil, nil + } + Ke = K[:keyLen] + Km = K[keyLen:] + hash.Reset() + if _, err := hash.Write(Km); err != nil { + return nil, nil + } + 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) + if _, err := mac.Write(msg); err != nil { + return nil + } + if _, err := mac.Write(shared); err != nil { + return nil + } + tag := mac.Sum(nil) + return tag +} + +// 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 +} + +// Decrypt decrypts an ECIES ciphertext. +func EciesDecrypt(prv PrivateKey, c, s1, s2 []byte) (m []byte, err error) { + if len(c) == 0 { + return nil, ErrInvalidMessage + } + pub := prv.Public() + params, err := pubkeyParams(pub) + if err != nil { + return nil, err + } + + hash := params.Hash() + + var ( + rLen int + hLen int = hash.Size() + mStart int + mEnd int + ) + + switch c[0] { + case 2, 3, 4: + rLen = (pub.Curve.Params().BitSize + 7) / 4 + if len(c) < (rLen + hLen + 1) { + return nil, ErrInvalidMessage + } + default: + return nil, ErrInvalidPublicKey + } + + mStart = rLen + mEnd = len(c) - hLen + + R := new(ecdsa.PublicKey) + R.Curve = pub.Curve + R.X, R.Y = elliptic.Unmarshal(R.Curve, c[:rLen]) + if R.X == nil { + return nil, ErrInvalidPublicKey + } + + z, err := prv.GenerateShared(R, params.KeyLen, params.KeyLen) + if err != nil { + return nil, err + } + Ke, Km := deriveKeys(hash, z, s1, params.KeyLen) + if Ke == nil || Km == nil { + return nil, ErrInvalidPublicKey + } + + d := messageTag(params.Hash, Km, c[mStart:mEnd], s2) + if d == nil { + return nil, ErrInvalidMessage + } + if subtle.ConstantTimeCompare(c[mEnd:], d) != 1 { + return nil, ErrInvalidMessage + } + + return symDecrypt(params, Ke, c[mStart:mEnd]) +} diff --git a/internal/ecies_params_ethereum.go b/internal/ecies_params_ethereum.go new file mode 100644 index 0000000..ace72bb --- /dev/null +++ b/internal/ecies_params_ethereum.go @@ -0,0 +1,135 @@ +// Copyright (c) 2013 Kyle Isom +// 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 internal + +// This file contains parameters for ECIES encryption, specifying the +// symmetric encryption and HMAC parameters. + +import ( + "crypto" + "crypto/aes" + "crypto/cipher" + "crypto/ecdsa" + "crypto/elliptic" + "crypto/sha256" + "crypto/sha512" + "fmt" + "hash" + + ethcrypto "github.com/ethereum/go-ethereum/crypto" +) + +var ( + DefaultCurve = ethcrypto.S256() + ErrUnsupportedECDHAlgorithm = fmt.Errorf("ecies: unsupported ECDH algorithm") + ErrUnsupportedECIESParameters = fmt.Errorf("ecies: unsupported ECIES parameters") + ErrInvalidKeyLen = fmt.Errorf("ecies: invalid key size (> %d) in ECIESParams", maxKeyLen) +) + +// KeyLen 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 { + Hash func() hash.Hash // hash function + hashAlgo crypto.Hash + Cipher func([]byte) (cipher.Block, error) // symmetric cipher + BlockSize int // block size of symmetric cipher + KeyLen int // length of symmetric key +} + +// Standard ECIES parameters: +// * ECIES using AES128 and HMAC-SHA-256-16 +// * ECIES using AES256 and HMAC-SHA-256-32 +// * ECIES using AES256 and HMAC-SHA-384-48 +// * ECIES using AES256 and HMAC-SHA-512-64 + +var ( + ECIES_AES128_SHA256 = &ECIESParams{ + Hash: sha256.New, + hashAlgo: crypto.SHA256, + Cipher: aes.NewCipher, + BlockSize: aes.BlockSize, + KeyLen: 16, + } + + ECIES_AES256_SHA256 = &ECIESParams{ + Hash: sha256.New, + hashAlgo: crypto.SHA256, + Cipher: aes.NewCipher, + BlockSize: aes.BlockSize, + KeyLen: 32, + } + + ECIES_AES256_SHA384 = &ECIESParams{ + Hash: sha512.New384, + hashAlgo: crypto.SHA384, + Cipher: aes.NewCipher, + BlockSize: aes.BlockSize, + KeyLen: 32, + } + + ECIES_AES256_SHA512 = &ECIESParams{ + Hash: sha512.New, + hashAlgo: crypto.SHA512, + Cipher: aes.NewCipher, + BlockSize: aes.BlockSize, + KeyLen: 32, + } +) + +var paramsFromCurve = map[elliptic.Curve]*ECIESParams{ + ethcrypto.S256(): ECIES_AES128_SHA256, + elliptic.P256(): ECIES_AES128_SHA256, + elliptic.P384(): ECIES_AES256_SHA384, + elliptic.P521(): ECIES_AES256_SHA512, +} + +func AddParamsForCurve(curve elliptic.Curve, params *ECIESParams) { + paramsFromCurve[curve] = params +} + +// ParamsFromCurve selects parameters optimal for the selected elliptic curve. +// Only the curves P256, P384, and P512 are supported. +func ParamsFromCurve(curve elliptic.Curve) (params *ECIESParams) { + return paramsFromCurve[curve] +} + +func pubkeyParams(key *ecdsa.PublicKey) (*ECIESParams, error) { + params := ParamsFromCurve(key.Curve) + if params == nil { + return nil, ErrUnsupportedECIESParameters + } + if params.KeyLen > maxKeyLen { + return nil, ErrInvalidKeyLen + } + return params, nil +}