Skip to content

Commit

Permalink
Support ed25519 signature algorithm
Browse files Browse the repository at this point in the history
Considering the efforts to give ed25519 support
in fabric and fabric-gateway, this commit intends
to provide ed25519 support for fabric-ca.

With these efforts, cryptogen can generate ed25519
keys for the CA, motivating these changes to keep
compatibility in terms of crypto support.

Fabric main branch current version (3.0.0) does not
support RSA anymore. For that reason, I had to manually
merge the ed25519 pull request
(hyperledger/fabric#3343)
with fabric v1.4.11. The adapated version replaces
fabric v1.4.11 in 'go.mod'.

Signed-off-by: Johann Westphall <johannwestphall@gmail.com>
  • Loading branch information
johannww committed Jun 7, 2024
1 parent e9c0d7f commit b5c190c
Show file tree
Hide file tree
Showing 16 changed files with 530 additions and 26 deletions.
3 changes: 2 additions & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ require (
github.com/gorilla/mux v1.8.0
github.com/grantae/certinfo v0.0.0-20170412194111-59d56a35515b
github.com/hyperledger/fabric-amcl v0.0.0-20230602173724-9e02669dceb2
github.com/hyperledger/fabric-lib-go v1.1.2
github.com/hyperledger/fabric-lib-go v1.1.3-0.20240523144151-25edd1eaf5f5
github.com/jinzhu/copier v0.3.5
github.com/jmoiron/sqlx v1.2.0
github.com/kisielk/sqlstruct v0.0.0-20201105191214-5f3e10d3ab46
Expand Down Expand Up @@ -84,3 +84,4 @@ require (
gopkg.in/yaml.v3 v3.0.1 // indirect
rsc.io/tmplfunc v0.0.3 // indirect
)

4 changes: 2 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -272,8 +272,8 @@ github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpO
github.com/hudl/fargo v1.3.0/go.mod h1:y3CKSmjA+wD2gak7sUSXTAoopbhU08POFhmITJgmKTg=
github.com/hyperledger/fabric-amcl v0.0.0-20230602173724-9e02669dceb2 h1:B1Nt8hKb//KvgGRprk0h1t4lCnwhE9/ryb1WqfZbV+M=
github.com/hyperledger/fabric-amcl v0.0.0-20230602173724-9e02669dceb2/go.mod h1:X+DIyUsaTmalOpmpQfIvFZjKHQedrURQ5t4YqquX7lE=
github.com/hyperledger/fabric-lib-go v1.1.2 h1:3eHwudGZC5Ex7go5UAzVKhpF34gypPZGfSZksBKLWvE=
github.com/hyperledger/fabric-lib-go v1.1.2/go.mod h1:SHNCq8AB0VpHAmvJEtdbzabv6NNV1F48JdmDihasBjc=
github.com/hyperledger/fabric-lib-go v1.1.3-0.20240523144151-25edd1eaf5f5 h1:RPWTL5wxAb+xDOrsCU3QYZP65305F8v3PaOyzdbPVMU=
github.com/hyperledger/fabric-lib-go v1.1.3-0.20240523144151-25edd1eaf5f5/go.mod h1:SHNCq8AB0VpHAmvJEtdbzabv6NNV1F48JdmDihasBjc=
github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM=
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
Expand Down
11 changes: 11 additions & 0 deletions lib/ca.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import (
"bytes"
"crypto/dsa"
"crypto/ecdsa"
"crypto/ed25519"
"crypto/rsa"
"crypto/x509"
"encoding/pem"
Expand Down Expand Up @@ -1179,6 +1180,16 @@ func validateMatchingKeys(cert *x509.Certificate, keyFile string) error {
if privKey.PublicKey.X.Cmp(pubKey.X) != 0 {
return errors.New("Public key and private key do not match")
}
case ed25519.PublicKey:
privKey, err := util.GetEd25519PrivateKey(keyPEM)
if err != nil {
return err
}

publicFromPriv := privKey.Public().(ed25519.PublicKey)
if !publicFromPriv.Equal(pubKey) {
return errors.New("Public key and private key do not match")
}
}

return nil
Expand Down
13 changes: 13 additions & 0 deletions util/csp.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ package util
import (
"crypto"
"crypto/ecdsa"
"crypto/ed25519"
"crypto/rsa"
"crypto/tls"
"crypto/x509"
Expand Down Expand Up @@ -135,6 +136,8 @@ func getBCCSPKeyOpts(kr *csr.KeyRequest, ephemeral bool) (opts bccsp.KeyGenOpts,
default:
return nil, errors.Errorf("Invalid ECDSA key size: %d", kr.Size())
}
case "ed25519":
return &bccsp.ED25519KeyGenOpts{Temporary: ephemeral}, nil
default:
return nil, errors.Errorf("Invalid algorithm: %s", kr.Algo())
}
Expand Down Expand Up @@ -228,6 +231,16 @@ func ImportBCCSPKeyFromPEM(keyFile string, myCSP bccsp.BCCSP, temporary bool) (b
return sk, nil
case *rsa.PrivateKey:
return nil, errors.Errorf("Failed to import RSA key from %s; RSA private key import is not supported", keyFile)
case ed25519.PrivateKey:
priv, err := PrivateKeyToDER(&key)
if err != nil {
return nil, errors.WithMessage(err, fmt.Sprintf("Failed to convert Ed25519 private key for '%s'", keyFile))
}
sk, err := myCSP.KeyImport(priv, &bccsp.ED25519PrivateKeyImportOpts{Temporary: temporary})
if err != nil {
return nil, errors.WithMessage(err, fmt.Sprintf("Failed to import Ed25519 private key for '%s'", keyFile))
}
return sk, nil
default:
return nil, errors.Errorf("Failed to import key from %s: invalid secret key type", keyFile)
}
Expand Down
100 changes: 92 additions & 8 deletions util/keys.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,9 @@ limitations under the License.
package util

import (
"crypto"
"crypto/ecdsa"
"crypto/ed25519"
"crypto/elliptic"
"crypto/rand"
"crypto/rsa"
Expand Down Expand Up @@ -66,12 +68,20 @@ func oidFromNamedCurve(curve elliptic.Curve) (asn1.ObjectIdentifier, bool) {
}

// PrivateKeyToDER marshals a private key to der
func PrivateKeyToDER(privateKey *ecdsa.PrivateKey) ([]byte, error) {
func PrivateKeyToDER(privateKey crypto.PrivateKey) ([]byte, error) {
if privateKey == nil {
return nil, errors.New("Invalid ecdsa private key. It must be different from nil")
}

return x509.MarshalECPrivateKey(privateKey)
switch key := privateKey.(type) {
// Fabric supports ECDSA and ED25519 at the moment.
case *ecdsa.PrivateKey:
return x509.MarshalECPrivateKey(privateKey.(*ecdsa.PrivateKey))
case *ed25519.PrivateKey:
return x509.MarshalPKCS8PrivateKey(*privateKey.(*ed25519.PrivateKey))
default:
return nil, fmt.Errorf("found unknown private key type (%T) in marshaling", key)
}
}

// PrivateKeyToPEM converts the private key to PEM format.
Expand Down Expand Up @@ -122,14 +132,30 @@ func PrivateKeyToPEM(privateKey interface{}, pwd []byte) ([]byte, error) {

pkcs8Bytes, err := asn1.Marshal(pkcs8Key)
if err != nil {
return nil, fmt.Errorf("error marshaling EC key to asn1 [%s]", err)
return nil, fmt.Errorf("error marshaling EC key to asn1: [%s]", err)
}
return pem.EncodeToMemory(
&pem.Block{
Type: "PRIVATE KEY",
Bytes: pkcs8Bytes,
},
), nil
case *ed25519.PrivateKey:
if k == nil {
return nil, errors.New("invalid ed25519 private key. It must be different from nil")
}

pkcs8Bytes, err := x509.MarshalPKCS8PrivateKey(*k)
if err != nil {
return nil, fmt.Errorf("error marshaling ED key to asn1: [%s]", err)
}
return pem.EncodeToMemory(
&pem.Block{
Type: "PRIVATE KEY",
Bytes: pkcs8Bytes,
},
), nil

case *rsa.PrivateKey:
if k == nil {
return nil, errors.New("Invalid rsa private key. It must be different from nil")
Expand All @@ -143,7 +169,7 @@ func PrivateKeyToPEM(privateKey interface{}, pwd []byte) ([]byte, error) {
},
), nil
default:
return nil, errors.New("Invalid key type. It must be *ecdsa.PrivateKey or *rsa.PrivateKey")
return nil, errors.New("Invalid key type. It must be *ecdsa.PrivateKey or *ed25519.PrivateKey or *rsa.PrivateKey")
}
}

Expand All @@ -170,15 +196,34 @@ func PrivateKeyToEncryptedPEM(privateKey interface{}, pwd []byte) ([]byte, error
raw,
pwd,
x509.PEMCipherAES256)
if err != nil {
return nil, err
}

return pem.EncodeToMemory(block), nil
case *ed25519.PrivateKey:
if k == nil {
return nil, errors.New("Invalid ed25519 private key. It must be different from nil")
}
raw, err := x509.MarshalPKCS8PrivateKey(*k)
if err != nil {
return nil, err
}

block, err := x509.EncryptPEMBlock(
rand.Reader,
"PRIVATE KEY",
raw,
pwd,
x509.PEMCipherAES256)
if err != nil {
return nil, err
}

return pem.EncodeToMemory(block), nil

default:
return nil, errors.New("Invalid key type. It must be *ecdsa.PrivateKey")
return nil, errors.New("invalid key type. It must be *ecdsa.PrivateKey or *ed25519.PrivateKey")
}
}

Expand All @@ -193,6 +238,8 @@ func DERToPrivateKey(der []byte) (key interface{}, err error) {
switch key.(type) {
case *rsa.PrivateKey, *ecdsa.PrivateKey:
return
case ed25519.PrivateKey:
return
default:
return nil, errors.New("Found unknown private key type in PKCS#8 wrapping")
}
Expand All @@ -202,7 +249,7 @@ func DERToPrivateKey(der []byte) (key interface{}, err error) {
return
}

return nil, errors.New("Invalid key type. The DER must contain an rsa.PrivateKey or ecdsa.PrivateKey")
return nil, errors.New("invalid key type. The DER must contain an rsa.PrivateKey or ecdsa.PrivateKey or an ed25519.PrivateKey")
}

// PEMtoPrivateKey unmarshals a pem to private key
Expand Down Expand Up @@ -320,6 +367,22 @@ func PublicKeyToPEM(publicKey interface{}, pwd []byte) ([]byte, error) {
Bytes: PubASN1,
},
), nil
case *ed25519.PublicKey:
if k == nil {
return nil, errors.New("invalid ed25519 public key. It must be different from nil")
}
PubASN1, err := x509.MarshalPKIXPublicKey(*k)
if err != nil {
return nil, err
}

return pem.EncodeToMemory(
&pem.Block{
Type: "PUBLIC KEY",
Bytes: PubASN1,
},
), nil

case *rsa.PublicKey:
if k == nil {
return nil, errors.New("Invalid rsa public key. It must be different from nil")
Expand All @@ -337,7 +400,7 @@ func PublicKeyToPEM(publicKey interface{}, pwd []byte) ([]byte, error) {
), nil

default:
return nil, errors.New("Invalid key type. It must be *ecdsa.PublicKey or *rsa.PublicKey")
return nil, errors.New("invalid key type. It must be *ecdsa.PublicKey or *ed25519.PublicKey or *rsa.PublicKey")
}
}

Expand Down Expand Up @@ -376,6 +439,7 @@ func PublicKeyToDER(publicKey interface{}) ([]byte, error) {
}

// PublicKeyToEncryptedPEM converts a public key to encrypted pem
// TODO: johann, refactor this later
func PublicKeyToEncryptedPEM(publicKey interface{}, pwd []byte) ([]byte, error) {
if publicKey == nil {
return nil, errors.New("Invalid public key. It must be different from nil")
Expand All @@ -394,6 +458,26 @@ func PublicKeyToEncryptedPEM(publicKey interface{}, pwd []byte) ([]byte, error)
return nil, err
}

block, err := x509.EncryptPEMBlock(
rand.Reader,
"PUBLIC KEY",
raw,
pwd,
x509.PEMCipherAES256)
if err != nil {
return nil, err
}

return pem.EncodeToMemory(block), nil
case *ed25519.PublicKey:
if k == nil {
return nil, errors.New("invalid ed25519 public key. It must be different from nil")
}
raw, err := x509.MarshalPKIXPublicKey(*k)
if err != nil {
return nil, err
}

block, err := x509.EncryptPEMBlock(
rand.Reader,
"PUBLIC KEY",
Expand All @@ -408,7 +492,7 @@ func PublicKeyToEncryptedPEM(publicKey interface{}, pwd []byte) ([]byte, error)
return pem.EncodeToMemory(block), nil

default:
return nil, errors.New("Invalid key type. It must be *ecdsa.PublicKey")
return nil, errors.New("invalid key type. It must be *ecdsa.PublicKey or *ed25519.PublicKey")
}
}

Expand Down
26 changes: 26 additions & 0 deletions util/util.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ package util
import (
"bytes"
"crypto/ecdsa"
"crypto/ed25519"
"crypto/rsa"
"crypto/x509"
"encoding/base64"
Expand Down Expand Up @@ -291,6 +292,8 @@ func GetECPrivateKey(raw []byte) (*ecdsa.PrivateKey, error) {
return key, nil
case *rsa.PrivateKey:
return nil, errors.New("Expecting EC private key but found RSA private key")
case ed25519.PrivateKey:
return nil, errors.New("Expecting EC private key but found Ed25519 private key")
default:
return nil, errors.New("Invalid private key type in PKCS#8 wrapping")
}
Expand All @@ -315,13 +318,36 @@ func GetRSAPrivateKey(raw []byte) (*rsa.PrivateKey, error) {
return nil, errors.New("Expecting RSA private key but found EC private key")
case *rsa.PrivateKey:
return key, nil
case ed25519.PrivateKey:
return nil, errors.New("Expecting RSA private key but found Ed25519 private key")
default:
return nil, errors.New("Invalid private key type in PKCS#8 wrapping")
}
}
return nil, errors.Wrap(err, "Failed parsing RSA private key")
}

func GetEd25519PrivateKey(raw []byte) (ed25519.PrivateKey, error) {
decoded, _ := pem.Decode(raw)
if decoded == nil {
return nil, errors.New("Failed to decode the PEM-encoded ECDSA key")
}
key, err2 := x509.ParsePKCS8PrivateKey(decoded.Bytes)
if err2 == nil {
switch key := key.(type) {
case ed25519.PrivateKey:
return key, nil
case *ecdsa.PrivateKey:
return nil, errors.New("Expecting Ed25519 private key but found EC private key")
case *rsa.PrivateKey:
return nil, errors.New("Expecting Ed25519 private key but found RSA private key")
default:
return nil, errors.New("Invalid private key type in PKCS#8 wrapping")
}
}
return nil, errors.Wrap(err2, "Failed parsing EC private key")
}

// B64Encode base64 encodes bytes
func B64Encode(buf []byte) string {
return base64.StdEncoding.EncodeToString(buf)
Expand Down
Loading

0 comments on commit b5c190c

Please sign in to comment.