Skip to content

Commit

Permalink
Add ed25519 support
Browse files Browse the repository at this point in the history
related to fabric pull request #3343,
regarding adding ed25519 support. Since the
bccsp folder was moved to fabric-lib-go,
I am moving the related changes here.

Signed-off-by: Johann Westphall <johannwestphall@gmail.com>
  • Loading branch information
johannww authored and denyeart committed Apr 30, 2024
1 parent 2d80476 commit 8846300
Show file tree
Hide file tree
Showing 14 changed files with 710 additions and 15 deletions.
50 changes: 50 additions & 0 deletions bccsp/opts.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,9 @@ const (
// ECDSAReRand ECDSA key re-randomization
ECDSAReRand = "ECDSA_RERAND"

// ED25519 Algorithm
ED25519 = "ED25519"

// RSA at the default security level.
// Each BCCSP may or may not support default security level. If not supported than
// an error will be returned.
Expand Down Expand Up @@ -90,6 +93,21 @@ func (opts *ECDSAKeyGenOpts) Ephemeral() bool {
return opts.Temporary
}

type ED25519KeyGenOpts struct {
Temporary bool
}

// Algorithm returns the key generation algorithm identifier (to be used).
func (opts *ED25519KeyGenOpts) Algorithm() string {
return ED25519
}

// Ephemeral returns true if the key to generate has to be ephemeral,
// false otherwise.
func (opts *ED25519KeyGenOpts) Ephemeral() bool {
return opts.Temporary
}

// ECDSAPKIXPublicKeyImportOpts contains options for ECDSA public key importation in PKIX format
type ECDSAPKIXPublicKeyImportOpts struct {
Temporary bool
Expand Down Expand Up @@ -139,6 +157,38 @@ func (opts *ECDSAGoPublicKeyImportOpts) Ephemeral() bool {
return opts.Temporary
}

// ED25519PrivateKeyImportOpts contains options for ED25519 key importation from ed25519.PublicKey
type ED25519PrivateKeyImportOpts struct {
Temporary bool
}

// Algorithm returns the key importation algorithm identifier (to be used).
func (opts *ED25519PrivateKeyImportOpts) Algorithm() string {
return ED25519
}

// Ephemeral returns true if the key to generate has to be ephemeral,
// false otherwise.
func (opts *ED25519PrivateKeyImportOpts) Ephemeral() bool {
return opts.Temporary
}

// ED25519GoPublicKeyImportOpts contains options for ED25519 key importation from ed25519.PublicKey
type ED25519GoPublicKeyImportOpts struct {
Temporary bool
}

// Algorithm returns the key importation algorithm identifier (to be used).
func (opts *ED25519GoPublicKeyImportOpts) Algorithm() string {
return ED25519
}

// Ephemeral returns true if the key to generate has to be ephemeral,
// false otherwise.
func (opts *ED25519GoPublicKeyImportOpts) Ephemeral() bool {
return opts.Temporary
}

// ECDSAReRandKeyOpts contains options for ECDSA key re-randomization.
type ECDSAReRandKeyOpts struct {
Temporary bool
Expand Down
15 changes: 11 additions & 4 deletions bccsp/signer/signer.go
Original file line number Diff line number Diff line change
Expand Up @@ -61,9 +61,16 @@ func (s *bccspCryptoSigner) Public() crypto.PublicKey {
return s.pk
}

// Sign signs digest with the private key, possibly using entropy from rand.
// Sign signs digest or the full message with the private key,
// possibly using entropy from rand.
// For an (EC)DSA key, it should be a DER-serialised, ASN.1 signature
// structure.
// structure.For an ED25519 key, it should be a signature compatible
// to the with the RFC 8032.
//
// If (EC)DSA signature, the hash must be passed as the "digestOrMsg"
// parameter. Golang requires the full message to sign with the
// built-in ED25519 library. Therefore, the full message must be
// passed as the "digestOrMsg" parameter.
//
// Hash implements the SignerOpts interface and, in most cases, one can
// simply pass in the hash function used as opts. Sign may also attempt
Expand All @@ -73,6 +80,6 @@ func (s *bccspCryptoSigner) Public() crypto.PublicKey {
// Note that when a signature of a hash of a larger message is needed,
// the caller is responsible for hashing the larger message and passing
// the hash (as digest) and the hash function (as opts) to Sign.
func (s *bccspCryptoSigner) Sign(rand io.Reader, digest []byte, opts crypto.SignerOpts) ([]byte, error) {
return s.csp.Sign(s.key, digest, opts)
func (s *bccspCryptoSigner) Sign(rand io.Reader, digestOrMsg []byte, opts crypto.SignerOpts) ([]byte, error) {
return s.csp.Sign(s.key, digestOrMsg, opts)
}
40 changes: 40 additions & 0 deletions bccsp/sw/ed25519.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
/*
Copyright IBM Corp. All Rights Reserved.
SPDX-License-Identifier: Apache-2.0
*/
package sw

import (
"crypto/ed25519"

"github.com/hyperledger/fabric-lib-go/bccsp"
)

func signED25519(k *ed25519.PrivateKey, msg []byte, opts bccsp.SignerOpts) ([]byte, error) {
signature := ed25519.Sign(*k, msg)
return signature, nil
}

func verifyED25519(k *ed25519.PublicKey, signature, msg []byte, opts bccsp.SignerOpts) (bool, error) {
return ed25519.Verify(*k, msg, signature), nil
}

type ed25519Signer struct{}

func (s *ed25519Signer) Sign(k bccsp.Key, msg []byte, opts bccsp.SignerOpts) ([]byte, error) {
return signED25519(k.(*ed25519PrivateKey).privKey, msg, opts)
}

type ed25519PrivateKeyVerifier struct{}

func (v *ed25519PrivateKeyVerifier) Verify(k bccsp.Key, signature, msg []byte, opts bccsp.SignerOpts) (bool, error) {
castedKey, _ := (k.(*ed25519PrivateKey).privKey.Public()).(ed25519.PublicKey)
return verifyED25519(&castedKey, signature, msg, opts)
}

type ed25519PublicKeyKeyVerifier struct{}

func (v *ed25519PublicKeyKeyVerifier) Verify(k bccsp.Key, signature, msg []byte, opts bccsp.SignerOpts) (bool, error) {
return verifyED25519(k.(*ed25519PublicKey).pubKey, signature, msg, opts)
}
138 changes: 138 additions & 0 deletions bccsp/sw/ed25519_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
/*
Copyright IBM Corp. All Rights Reserved.
SPDX-License-Identifier: Apache-2.0
*/

package sw

import (
"crypto/ed25519"
"crypto/rand"
"crypto/sha256"
"crypto/x509"
"testing"

"github.com/stretchr/testify/require"
)

func TestVerifyED25519(t *testing.T) {
t.Parallel()

// Generate a key
_, lowLevelKey, err := ed25519.GenerateKey(rand.Reader)
require.NoError(t, err)

msg := []byte("hello world")
sigma, err := signED25519(&lowLevelKey, msg, nil)
require.NoError(t, err)

castedKey, _ := lowLevelKey.Public().(ed25519.PublicKey)
valid, err := verifyED25519(&castedKey, sigma, msg, nil)
require.NoError(t, err)
require.True(t, valid)
}

func TestEd25519SignerSign(t *testing.T) {
t.Parallel()

signer := &ed25519Signer{}
verifierPrivateKey := &ed25519PrivateKeyVerifier{}
verifierPublicKey := &ed25519PublicKeyKeyVerifier{}

// Generate a key
_, lowLevelKey, err := ed25519.GenerateKey(rand.Reader)
require.NoError(t, err)
k := &ed25519PrivateKey{&lowLevelKey}
pk, err := k.PublicKey()
require.NoError(t, err)

// Sign
msg := []byte("Hello World")
sigma, err := signer.Sign(k, msg, nil)
require.NoError(t, err)
require.NotNil(t, sigma)

// Verify
castedKey, _ := lowLevelKey.Public().(ed25519.PublicKey)
valid, err := verifyED25519(&castedKey, sigma, msg, nil)
require.NoError(t, err)
require.True(t, valid)

valid, err = verifierPrivateKey.Verify(k, sigma, msg, nil)
require.NoError(t, err)
require.True(t, valid)

valid, err = verifierPublicKey.Verify(pk, sigma, msg, nil)
require.NoError(t, err)
require.True(t, valid)
}

func TestEd25519PrivateKey(t *testing.T) {
t.Parallel()

_, lowLevelKey, err := ed25519.GenerateKey(rand.Reader)
require.NoError(t, err)
k := &ed25519PrivateKey{&lowLevelKey}

require.False(t, k.Symmetric())
require.True(t, k.Private())

_, err = k.Bytes()
require.Error(t, err)
require.Contains(t, err.Error(), "Not supported.")

k.privKey = nil
ski := k.SKI()
require.Nil(t, ski)

k.privKey = &lowLevelKey
ski = k.SKI()
raw := k.privKey.Public().(ed25519.PublicKey)
hash := sha256.New()
hash.Write(raw)
ski2 := hash.Sum(nil)
require.Equal(t, ski2, ski, "SKI is not computed in the right way.")

pk, err := k.PublicKey()
require.NoError(t, err)
require.NotNil(t, pk)
ed25519PK, ok := pk.(*ed25519PublicKey)
require.True(t, ok)
castedKey, _ := lowLevelKey.Public().(ed25519.PublicKey)
require.Equal(t, &castedKey, ed25519PK.pubKey)
}

func TestEd25519PublicKey(t *testing.T) {
t.Parallel()

_, lowLevelKey, err := ed25519.GenerateKey(rand.Reader)
require.NoError(t, err)
castedKey, _ := lowLevelKey.Public().(ed25519.PublicKey)
k := &ed25519PublicKey{&castedKey}

require.False(t, k.Symmetric())
require.False(t, k.Private())

k.pubKey = nil
ski := k.SKI()
require.Nil(t, ski)

k.pubKey = &castedKey
ski = k.SKI()
raw := *(k.pubKey)
hash := sha256.New()
hash.Write(raw)
ski2 := hash.Sum(nil)
require.Equal(t, ski, ski2, "SKI is not computed in the right way.")

pk, err := k.PublicKey()
require.NoError(t, err)
require.Equal(t, k, pk)

bytes, err := k.Bytes()
require.NoError(t, err)
bytes2, err := x509.MarshalPKIXPublicKey(*k.pubKey)
require.NoError(t, err)
require.Equal(t, bytes2, bytes, "bytes are not computed in the right way.")
}
109 changes: 109 additions & 0 deletions bccsp/sw/ed25519key.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
/*
Copyright IBM Corp. All Rights Reserved.
SPDX-License-Identifier: Apache-2.0
*/
package sw

import (
"crypto/ed25519"
"crypto/sha256"
"crypto/x509"
"errors"
"fmt"

"github.com/hyperledger/fabric-lib-go/bccsp"
)

type ed25519PrivateKey struct {
privKey *ed25519.PrivateKey
}

// Bytes converts this key to its byte representation,
// if this operation is allowed.
func (k *ed25519PrivateKey) Bytes() ([]byte, error) {
return nil, errors.New("Not supported.")
}

// SKI returns the subject key identifier of this key.
func (k *ed25519PrivateKey) SKI() []byte {
if k.privKey == nil {
return nil
}

// Marshall the public key
raw := k.privKey.Public().(ed25519.PublicKey)

// Hash it
hash := sha256.New()
hash.Write(raw)
return hash.Sum(nil)
}

// Symmetric returns true if this key is a symmetric key,
// false if this key is asymmetric
func (k *ed25519PrivateKey) Symmetric() bool {
return false
}

// Private returns true if this key is a private key,
// false otherwise.
func (k *ed25519PrivateKey) Private() bool {
return true
}

// PublicKey returns the corresponding public key part of an asymmetric public/private key pair.
// This method returns an error in symmetric key schemes.
func (k *ed25519PrivateKey) PublicKey() (bccsp.Key, error) {
castedKey, ok := k.privKey.Public().(ed25519.PublicKey)
if !ok {
return nil, errors.New("Error casting ed25519 public key")
}
return &ed25519PublicKey{&castedKey}, nil
}

type ed25519PublicKey struct {
pubKey *ed25519.PublicKey
}

// Bytes converts this key to its byte representation,
// if this operation is allowed.
func (k *ed25519PublicKey) Bytes() (raw []byte, err error) {
raw, err = x509.MarshalPKIXPublicKey(*k.pubKey)
if err != nil {
return nil, fmt.Errorf("Failed marshalling key [%s]", err)
}
return
}

// SKI returns the subject key identifier of this key.
func (k *ed25519PublicKey) SKI() []byte {
if k.pubKey == nil {
return nil
}

raw := *(k.pubKey)

// Hash it
hash := sha256.New()
hash.Write(raw)
return hash.Sum(nil)
}

// Symmetric returns true if this key is a symmetric key,
// false if this key is asymmetric
func (k *ed25519PublicKey) Symmetric() bool {
return false
}

// Private returns true if this key is a private key,
// false otherwise.
func (k *ed25519PublicKey) Private() bool {
return false
}

// PublicKey returns the corresponding public key part of an asymmetric public/private key pair.
// This method returns an error in symmetric key schemes.
func (k *ed25519PublicKey) PublicKey() (bccsp.Key, error) {
return k, nil
}
Loading

0 comments on commit 8846300

Please sign in to comment.