Skip to content

Commit

Permalink
Merge pull request perlin-network#212 from perlin-network/signature-s…
Browse files Browse the repository at this point in the history
…cheme

Separate out signature schemes from identity keypairs.
  • Loading branch information
iwasaki-kenta committed Feb 22, 2019
2 parents fd8fe72 + 0854cdc commit e7b0ac2
Show file tree
Hide file tree
Showing 12 changed files with 170 additions and 161 deletions.
36 changes: 25 additions & 11 deletions docs/src/identities.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@ Most likely, you would want a cryptographic ID for your node.

As of right now, there exists a few identity schemes which Noise provides built-in support for that you may use on the get-go for your p2p application:

1. Ed25519 identities w/ EdDSA signatures
2. S/Kademlia-compatible Ed25519 identities w/ EdDSA signatures
1. Ed25519 identities
2. S/Kademlia-compatible Ed25519 identities

You may additionally create/implement your own identity schemes that any node/peer may support in Noise by simply having your scheme implement the following interface:

Expand All @@ -24,13 +24,10 @@ type Keypair interface {
ID() []byte
PublicKey() []byte
PrivateKey() []byte

Sign(buf []byte) ([]byte, error)
Verify(publicKeyBuf []byte, buf []byte, signature []byte) error
}
```

Should the identity scheme you wish to implement not have any associated signature scheme to it, or any sort of `PrivateKey()` or `PublicKey()` or `ID()` associated to it, you may simply stub out those functions and ignore their implementation.
Should the identity scheme you wish to implement not have any sort of `PrivateKey()` or `PublicKey()` or `ID()` associated to it, you may simply stub out those functions and ignore their implementation.

After picking/implementing an identity scheme of your choice for your p2p application, it is simple to have your node adopt it as follows:

Expand Down Expand Up @@ -61,16 +58,33 @@ params.Keys = skademlia.LoadKeys([]byte{...}, skademlia.DefaultC1, skademlia.Def

## Signing/Verifying Messages

Given a node instance instantiated with an identity scheme paired with a signature scheme,
you may sign/verify raw arrays of bytes to see whether or not a signature was generated
by a specified public key like so:
Signature schemes are stubbed out into an interface which you could implement to integrate
cryptographic signatures into supported protocol building blocks Noise provides.

All signature schemes implement the following interface:

```go
type Scheme interface {
Sign(privateKey, messageBuf []byte) ([]byte, error)
Verify(publicKeyBuf, messageBuf, signatureBuf []byte) error
}
```

As of right now, Noise provides off-the-shelf the following signature schemes:
- EdDSA Signature Scheme (assembly-optimized)

You may use a signature scheme to sign/verify raw arrays of bytes to see
whether or not a signature was generated by a specified public key like so:

```go
import "github.com/perlin-network/noise/signature/eddsa"

var node *noise.Node

message := "We're going to sign this message with our node!"

signature, err := node.Keys.Sign(message)
// Sign a message using the EdDSA signature scheme.
signature, err := eddsa.Sign(node.Keys.PrivateKey(), []byte(message))
if err != nil {
panic("failed to sign the message")
}
Expand All @@ -80,5 +94,5 @@ fmt.Println("Signature:", signature)
// Now let's verify that the signature is under our own nodes identity!

fmt.Println("Is the signature valid?",
node.Keys.Verify(node.Keys.PublicKey(), message, signature))
eddsa.Verify(node.Keys.PublicKey(), []byte(message), signature))
```
5 changes: 5 additions & 0 deletions docs/src/skademlia.md
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,11 @@ block.WithC2(int(16))
block.WithPrefixDiffLen(int(128))
block.WithPrefixDiffMaxLen(int(32))

// Additionally, have all S/Kademlia messages and future
// messages be appended with EdDSA signatures that are
// validated for every incoming message.
block.WithSignatureScheme(eddsa.New())

// Register the protocol block and enforce it on our node.
protocol.New().Register(block).Enforce(node)
```
Expand Down
20 changes: 0 additions & 20 deletions identity/ed25519/mod.go
Original file line number Diff line number Diff line change
Expand Up @@ -50,26 +50,6 @@ func (p *Keypair) PublicKey() []byte {
return p.publicKey
}

func (p *Keypair) Sign(buf []byte) ([]byte, error) {
if len(p.privateKey) != edwards25519.PrivateKeySize {
return nil, errors.Errorf("edwards25519: private key expected to be %d bytes, but is %d bytes", edwards25519.PrivateKeySize, len(p.privateKey))
}

return edwards25519.Sign(p.privateKey, buf), nil
}

func (p *Keypair) Verify(publicKeyBuf []byte, buf []byte, signature []byte) error {
if len(publicKeyBuf) != edwards25519.PublicKeySize {
return errors.Errorf("edwards25519: public key expected to be %d bytes, but is %d bytes", edwards25519.PublicKeySize, len(publicKeyBuf))
}

if edwards25519.Verify(publicKeyBuf, buf, signature) {
return nil
} else {
return errors.New("unable to verify signature")
}
}

func (p *Keypair) String() string {
return fmt.Sprintf("Ed25519(public: %s, private: %s)", hex.EncodeToString(p.PublicKey()), hex.EncodeToString(p.PrivateKey()))
}
Expand Down
70 changes: 0 additions & 70 deletions identity/ed25519/mod_test.go
Original file line number Diff line number Diff line change
@@ -1,54 +1,11 @@
package ed25519_test

import (
"crypto/rand"
"github.com/perlin-network/noise/identity/ed25519"
"github.com/stretchr/testify/assert"
"testing"
)

func BenchmarkSign(b *testing.B) {
p := ed25519.RandomKeys()

message := make([]byte, 32)
if _, err := rand.Read(message); err != nil {
panic(err)
}

b.ResetTimer()

for i := 0; i < b.N; i++ {
sig, err := p.Sign(message)
if err != nil || len(sig) == 0 {
panic("signing failed")
}
}
}

func BenchmarkVerify(b *testing.B) {
p := ed25519.RandomKeys()

message := make([]byte, 32)
if _, err := rand.Read(message); err != nil {
panic(err)
}

publicKey := p.PublicKey()

b.ResetTimer()

sig, err := p.Sign(message)
if err != nil {
panic(err)
}

for i := 0; i < b.N; i++ {
if err := p.Verify(publicKey, message, sig); err != nil {
panic("verification failed")
}
}
}

func TestEd25519(t *testing.T) {
t.Parallel()
p := ed25519.RandomKeys()
Expand All @@ -58,35 +15,8 @@ func TestEd25519(t *testing.T) {
assert.True(t, len(p.PublicKey()) > 0)
assert.True(t, len(p.String()) > 0)

message := []byte("test message")
// sign with a bad key should have yield signature with 0 length

// length of signature should not be 0
sig, err := p.Sign(message)
assert.Nil(t, err)
assert.True(t, len(sig) > 0)

// correct message should pass verify check
err = p.Verify(publicKey, message, sig)
assert.Nil(t, err)

// wrong public key should fail verify check
err = p.Verify([]byte("bad key"), message, sig)
assert.NotNil(t, err)

// wrong message should fail verify check
wrongMessage := []byte("wrong message")
err = p.Verify(publicKey, wrongMessage, sig)
assert.NotNil(t, err)

// try reloading the private key, should make the same object
mgr := ed25519.LoadKeys(privateKey)
assert.NotNil(t, mgr)
assert.EqualValues(t, mgr.PublicKey(), publicKey)

// make sure signing is different
badMgr := ed25519.LoadKeys(ed25519.RandomKeys().PrivateKey())
badSig, err := badMgr.Sign(message)
assert.Nil(t, err)
assert.NotEqual(t, sig, badSig)
}
3 changes: 0 additions & 3 deletions identity/keypair.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,4 @@ type Keypair interface {
ID() []byte
PublicKey() []byte
PrivateKey() []byte

Sign(buf []byte) ([]byte, error)
Verify(publicKeyBuf []byte, buf []byte, signature []byte) error
}
43 changes: 43 additions & 0 deletions signature/eddsa/mod.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
package eddsa

import (
"github.com/perlin-network/noise/internal/edwards25519"
"github.com/perlin-network/noise/signature"
"github.com/pkg/errors"
)

var _ signature.Scheme = (*policy)(nil)

type policy struct{}

func (p policy) Sign(privateKey, messageBuf []byte) ([]byte, error) {
return Sign(privateKey, messageBuf)
}

func (p policy) Verify(publicKeyBuf, messageBuf, signatureBuf []byte) error {
return Verify(publicKeyBuf, messageBuf, signatureBuf)
}

func New() *policy {
return new(policy)
}

func Sign(privateKeyBuf, messageBuf []byte) ([]byte, error) {
if len(privateKeyBuf) != edwards25519.PrivateKeySize {
return nil, errors.Errorf("edwards25519: private key expected to be %d bytes, but is %d bytes", edwards25519.PrivateKeySize, len(privateKeyBuf))
}

return edwards25519.Sign(privateKeyBuf, messageBuf), nil
}

func Verify(publicKeyBuf, messageBuf, signature []byte) error {
if len(publicKeyBuf) != edwards25519.PublicKeySize {
return errors.Errorf("edwards25519: public key expected to be %d bytes, but is %d bytes", edwards25519.PublicKeySize, len(publicKeyBuf))
}

if edwards25519.Verify(publicKeyBuf, messageBuf, signature) {
return nil
} else {
return errors.New("unable to verify signature")
}
}
71 changes: 71 additions & 0 deletions signature/eddsa/mod_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
package eddsa

import (
"bytes"
"github.com/perlin-network/noise/internal/edwards25519"
"github.com/stretchr/testify/assert"
"testing"
"testing/quick"
)

func TestBadKey(t *testing.T) {
badKey := []byte("this is a bad key")
message := []byte("this is a message")

scheme := New()
_, err := scheme.Sign(badKey, message)
assert.Error(t, err)

_, err = Sign(badKey, message)
assert.Error(t, err)

err = scheme.Verify(badKey, message, []byte("random signature"))
assert.Error(t, err)

err = Verify(badKey, message, []byte("random signature"))
assert.Error(t, err)
}

func TestSignAndVerify(t *testing.T) {
scheme := New()

quick.Check(func(message []byte, bad []byte) bool {
publicKey, privateKey, err := edwards25519.GenerateKey(nil)
if err != nil {
return false
}

sign1, err := scheme.Sign(privateKey, message)
if err != nil {
return false
}

sign2, err := Sign(privateKey, message)
if err != nil {
return false
}

if !bytes.Equal(sign1, sign2) {
return false
}

if scheme.Verify(publicKey, message, sign2) != nil {
return false
}

if Verify(publicKey, message, sign1) != nil {
return false
}

// Check invalid signature
if scheme.Verify(publicKey, message, append(sign2, bad...)) != nil {
return false
}

if Verify(publicKey, message, append(sign1, bad...)) != nil {
return false
}

return true
}, nil)
}
6 changes: 6 additions & 0 deletions signature/scheme.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package signature

type Scheme interface {
Sign(privateKey, messageBuf []byte) ([]byte, error)
Verify(publicKeyBuf, messageBuf, signatureBuf []byte) error
}
20 changes: 0 additions & 20 deletions skademlia/keys.go
Original file line number Diff line number Diff line change
Expand Up @@ -112,26 +112,6 @@ func (p *Keypair) PublicKey() []byte {
return p.publicKey
}

func (p *Keypair) Sign(buf []byte) ([]byte, error) {
if len(p.privateKey) != edwards25519.PrivateKeySize {
return nil, errors.Errorf("ed25519: private key expected to be %d bytes, but is %d bytes", edwards25519.PrivateKeySize, len(p.privateKey))
}

return edwards25519.Sign(p.privateKey, buf), nil
}

func (p *Keypair) Verify(publicKeyBuf []byte, buf []byte, signature []byte) error {
if len(publicKeyBuf) != edwards25519.PublicKeySize {
return errors.Errorf("ed25519: public key expected to be %d bytes, but is %d bytes", edwards25519.PublicKeySize, len(publicKeyBuf))
}

if edwards25519.Verify(publicKeyBuf, buf, signature) {
return nil
} else {
return errors.New("unable to verify signature")
}
}

func (p *Keypair) String() string {
return fmt.Sprintf("S/Kademlia(public: %s, private: %s)", hex.EncodeToString(p.PublicKey()), hex.EncodeToString(p.PrivateKey()))
}
Expand Down
24 changes: 0 additions & 24 deletions skademlia/keys_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import (
"encoding/hex"
"fmt"
"github.com/perlin-network/noise/identity/ed25519"
"github.com/perlin-network/noise/internal/edwards25519"
"github.com/stretchr/testify/assert"
"golang.org/x/crypto/blake2b"
"testing"
Expand Down Expand Up @@ -50,29 +49,6 @@ func TestNewSKademliaIdentityFromPrivateKey(t *testing.T) {
}
}

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

data, err := randomBytes(1024)
assert.NoError(t, err)

privateKeyHex := "1946e455ca6072bcdfd3182799c2ceb1557c2a56c5f810478ac0eb279ad4c93e8e8b6a97551342fd70ec03bea8bae5b05bc5dc0f54b2721dff76f06fab909263"

privateKey, err := hex.DecodeString(privateKeyHex)
assert.NoError(t, err)

keys, err := LoadKeys(privateKey, DefaultC1, DefaultC2)
assert.NoError(t, err)
assert.NotNil(t, keys)

signature, err := keys.Sign([]byte(data))

assert.NoError(t, err)
assert.Len(t, signature, edwards25519.SignatureSize)

assert.Nil(t, keys.Verify(keys.publicKey, data, signature))
}

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

Expand Down
Loading

0 comments on commit e7b0ac2

Please sign in to comment.