Skip to content

Commit

Permalink
implement support for ed25519 (#504)
Browse files Browse the repository at this point in the history
* implement support for ed25519

* moving crypto operation to pkg

* increasing code coverage

* increase coverage

* refactor crypto folder

* extend code coverage

* fixing linting

* increasing coverage

* applying comments
  • Loading branch information
ggarri authored Feb 15, 2022
1 parent 16c9b8d commit 07095b9
Show file tree
Hide file tree
Showing 15 changed files with 670 additions and 168 deletions.
5 changes: 3 additions & 2 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,11 @@
## v21.12.2 (Unreleased)
### 🆕 Features
* Support for OIDC token custom claims `AUTH_OIDC_CUSTOM_CLAIMS` for tenant_id and permissions.

* Support for ED25519 keys (EdDSA signature scheme using SHA-512 and Curve25519)

### 🛠 Bug fixes
* Invalid authentication forwarded to downstream proxy nodes if QKM authentication is enabled.
* Fixed AWS client issue preventing racing condition on key state transition changes.
* Fixed AWS client issue preventing racing condition on key state transition changes.
* Fixed invalid request error on create Registry with empty payload
* Fixed forwarding of vault service 429 errors.

Expand Down
20 changes: 0 additions & 20 deletions pkg/crypto/ecdsa.go

This file was deleted.

62 changes: 62 additions & 0 deletions pkg/crypto/ecdsa/secp256k1.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
package ecdsa

import (
"crypto/ecdsa"
"fmt"
"math/big"

"github.com/ethereum/go-ethereum/crypto"
)

func CreateSecp256k1(importedPrivKey []byte) (privKey, pubKey []byte, err error) {
var ecdsaKey *ecdsa.PrivateKey
if importedPrivKey != nil {
ecdsaKey, err = crypto.ToECDSA(importedPrivKey)
if err != nil {
return nil, nil, err
}
} else {
ecdsaKey, err = crypto.GenerateKey()
if err != nil {
return nil, nil, err
}
}

privKey = crypto.FromECDSA(ecdsaKey)
pubKey = crypto.FromECDSAPub(&ecdsaKey.PublicKey)
return privKey, pubKey, nil
}

func SignSecp256k1(privKey, data []byte) ([]byte, error) {
if len(data) != crypto.DigestLength {
return nil, fmt.Errorf("data is required to be exactly %d bytes (%d)", crypto.DigestLength, len(data))
}

ecdsaPrivKey, err := crypto.ToECDSA(privKey)
if err != nil {
return nil, fmt.Errorf("failed to parse private key. %s", err.Error())
}

signature, err := crypto.Sign(data, ecdsaPrivKey)
if err != nil {
return nil, fmt.Errorf("failed to sign. %s", err.Error())
}

// We remove the recID from the signature (last byte).
return signature[:len(signature)-1], nil
}

func VerifySecp256k1Signature(publicKey, message, signature []byte) (bool, error) {
pubKey, err := crypto.UnmarshalPubkey(publicKey)
if err != nil {
return false, err
}
if len(signature) != 64 {
return false, fmt.Errorf("invalid secp256k1 signature length")
}

r := new(big.Int).SetBytes(signature[0:32])
s := new(big.Int).SetBytes(signature[32:64])

return ecdsa.Verify(pubKey, message, r, s), nil
}
21 changes: 0 additions & 21 deletions pkg/crypto/eddsa.go

This file was deleted.

68 changes: 68 additions & 0 deletions pkg/crypto/eddsa/babyjubjub.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
package eddsa

import (
"bytes"
"crypto/rand"
"fmt"

babyjubjub "github.com/consensys/gnark-crypto/ecc/bn254/twistededwards/eddsa"
"github.com/consensys/gnark-crypto/hash"
)

func CreateBabyjubjub(importedPrivKey []byte) (privKey, pubKey []byte, err error) {
babyJubJubPrivKey := babyjubjub.PrivateKey{}
if importedPrivKey != nil {
_, err = babyJubJubPrivKey.SetBytes(importedPrivKey)
if err != nil {
return nil, nil, err
}
} else {
seed := make([]byte, 32)
_, err = rand.Read(seed)
if err != nil {
return nil, nil, err
}

// Usually standards implementations of eddsa do not require the choice of a specific hash function (usually it's SHA256).
// Here we needed to allow the choice of the hash, so we can choose a hash function that is easily programmable in a snark circuit.
// Same hFunc should be used for sign and verify
babyJubJubPrivKey, err = babyjubjub.GenerateKey(bytes.NewReader(seed))
if err != nil {
return nil, nil, err
}
}

privKey = babyJubJubPrivKey.Bytes()
pubKey = babyJubJubPrivKey.Public().Bytes()
return privKey, pubKey, nil
}

func SignBabyjubjub(privKeyB, data []byte) ([]byte, error) {
privKey := babyjubjub.PrivateKey{}
_, err := privKey.SetBytes(privKeyB)
if err != nil {
return nil, fmt.Errorf("failed to parse private key. %s", err.Error())
}

signature, err := privKey.Sign(data, hash.MIMC_BN254.New("seed"))
if err != nil {
return nil, fmt.Errorf("failed to sign. %s", err.Error())
}

return signature, nil
}

func VerifyBabyJubJubSignature(publicKey, message, signature []byte) (bool, error) {
pubKey := babyjubjub.PublicKey{}
_, err := pubKey.SetBytes(publicKey)
if err != nil {
return false, err
}

verified, err := pubKey.Verify(signature, message, hash.MIMC_BN254.New("seed"))
if err != nil {
return false, err
}

return verified, nil
}
52 changes: 52 additions & 0 deletions pkg/crypto/eddsa/ed25519.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
package eddsa

import (
"bytes"
"crypto/ed25519"
"crypto/rand"
"fmt"
)

func CreateED25519(importedPrivKey []byte) (privKey, pubKey []byte, err error) {
// https://pkg.go.dev/crypto/ed25519#section-documentation
if importedPrivKey != nil {
if len(importedPrivKey) != ed25519.PrivateKeySize {
return nil, nil, fmt.Errorf("invalid private key value")
}
ed25519PrivKey := ed25519.PrivateKey(importedPrivKey)
pubKey = ed25519PrivKey.Public().(ed25519.PublicKey)
privKey = ed25519PrivKey
} else {
seed := make([]byte, 32)
if _, err = rand.Read(seed); err != nil {
return nil, nil, err
}

pubKey, privKey, err = ed25519.GenerateKey(bytes.NewReader(seed))
if err != nil {
return nil, nil, err
}
}

return privKey, pubKey, nil
}

func SignED25519(privKeyB, data []byte) ([]byte, error) {
if len(privKeyB) != ed25519.PrivateKeySize {
return nil, fmt.Errorf("invalid ED25519 private key length")
}
privKey := ed25519.PrivateKey(privKeyB)
signature := ed25519.Sign(privKey, data)
return signature, nil
}

func VerifyED25519Signature(publicKey, message, signature []byte) (bool, error) {
if len(publicKey) != ed25519.PublicKeySize {
return false, fmt.Errorf("invalid ED25519 public key length")
}
if len(signature) != ed25519.SignatureSize {
return false, fmt.Errorf("invalid ED25519 signature length")
}
pubKey := ed25519.PublicKey(publicKey)
return ed25519.Verify(pubKey, message, signature), nil
}
1 change: 1 addition & 0 deletions src/entities/algo.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ const (

Babyjubjub Curve = "babyjubjub"
Secp256k1 Curve = "secp256k1"
Curve25519 Curve = "curve25519"
)

type Algorithm struct {
Expand Down
6 changes: 5 additions & 1 deletion src/stores/connectors/keys/create_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,9 @@ import (
"testing"

"github.com/consensys/quorum-key-manager/pkg/errors"

"github.com/consensys/quorum-key-manager/src/auth/entities"
mock3 "github.com/consensys/quorum-key-manager/src/auth/mock"
entities2 "github.com/consensys/quorum-key-manager/src/entities"

"github.com/consensys/quorum-key-manager/src/infra/log/testutils"
mock2 "github.com/consensys/quorum-key-manager/src/stores/database/mock"
Expand All @@ -24,6 +24,10 @@ func TestCreateKey(t *testing.T) {
defer ctrl.Finish()

key := testutils2.FakeKey()
key.Algo = &entities2.Algorithm{
Type: entities2.Eddsa,
EllipticCurve: entities2.Curve25519,
}
expectedErr := fmt.Errorf("error")
attributes := testutils2.FakeAttributes()

Expand Down
4 changes: 4 additions & 0 deletions src/stores/connectors/keys/keys.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,5 +35,9 @@ func isSupportedAlgo(alg *entities.Algorithm) bool {
return true
}

if alg.Type == entities.Eddsa && alg.EllipticCurve == entities.Curve25519 {
return true
}

return false
}
37 changes: 33 additions & 4 deletions src/stores/store/keys/hashicorp/hashicorp.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,12 @@ func New(client hashicorp.PluginClient, logger log.Logger) *Store {
}

func (s *Store) Create(_ context.Context, id string, alg *entities2.Algorithm, attr *entities.Attributes) (*entities.Key, error) {
if !s.isSupportedAlgo(alg) {
errMessage := "invalid or not supported elliptic curve and signing algorithm for Hashicorp key creation"
s.logger.With("elliptic_curve", alg.EllipticCurve, "signing_algorithm", alg.Type).Error(errMessage)
return nil, errors.NotSupportedError(errMessage)
}

res, err := s.client.CreateKey(map[string]interface{}{
idLabel: id,
curveLabel: alg.EllipticCurve,
Expand All @@ -52,11 +58,16 @@ func (s *Store) Create(_ context.Context, id string, alg *entities2.Algorithm, a
s.logger.With("id", id).WithError(err).Error(errMessage)
return nil, errors.FromError(err).SetMessage(errMessage)
}

return parseAPISecretToKey(res)
}

func (s *Store) Import(_ context.Context, id string, privKey []byte, alg *entities2.Algorithm, attr *entities.Attributes) (*entities.Key, error) {
if !s.isSupportedAlgo(alg) {
errMessage := "invalid or not supported elliptic curve and signing algorithm for Hashicorp key import"
s.logger.With("elliptic_curve", alg.EllipticCurve, "signing_algorithm", alg.Type).Error(errMessage)
return nil, errors.NotSupportedError(errMessage)
}

res, err := s.client.ImportKey(map[string]interface{}{
idLabel: id,
curveLabel: alg.EllipticCurve,
Expand Down Expand Up @@ -165,7 +176,13 @@ func (s *Store) Destroy(_ context.Context, id string) error {
return nil
}

func (s *Store) Sign(_ context.Context, id string, data []byte, _ *entities2.Algorithm) ([]byte, error) {
func (s *Store) Sign(_ context.Context, id string, data []byte, alg *entities2.Algorithm) ([]byte, error) {
if !s.isSupportedAlgo(alg) {
errMessage := "invalid or not supported elliptic curve and signing algorithm for Hashicorp signing"
s.logger.With("elliptic_curve", alg.EllipticCurve, "signing_algorithm", alg.Type).Error(errMessage)
return nil, errors.NotSupportedError(errMessage)
}

logger := s.logger.With("id", id)

res, err := s.client.Sign(id, data)
Expand All @@ -185,10 +202,22 @@ func (s *Store) Sign(_ context.Context, id string, data []byte, _ *entities2.Alg
return signature, nil
}

func (s *Store) Encrypt(ctx context.Context, id string, data []byte) ([]byte, error) {
func (s *Store) Encrypt(_ context.Context, id string, data []byte) ([]byte, error) {
return nil, errors.ErrNotImplemented
}

func (s *Store) Decrypt(ctx context.Context, id string, data []byte) ([]byte, error) {
func (s *Store) Decrypt(_ context.Context, id string, data []byte) ([]byte, error) {
return nil, errors.ErrNotImplemented
}

func (s *Store) isSupportedAlgo(alg *entities2.Algorithm) bool {
if alg.Type == entities2.Ecdsa && alg.EllipticCurve == entities2.Secp256k1 {
return true
}

if alg.Type == entities2.Eddsa && alg.EllipticCurve == entities2.Babyjubjub {
return true
}

return false
}
Loading

0 comments on commit 07095b9

Please sign in to comment.