Skip to content

Commit

Permalink
Merge pull request #41 from nats-io/curve2
Browse files Browse the repository at this point in the history
unify KeyPair and Curve interfaces
  • Loading branch information
derekcollison authored Dec 15, 2022
2 parents 6086451 + 533e7a4 commit 47c7408
Show file tree
Hide file tree
Showing 7 changed files with 197 additions and 47 deletions.
48 changes: 26 additions & 22 deletions errors.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,28 +15,32 @@ package nkeys

// Errors
const (
ErrInvalidPrefixByte = nkeysError("nkeys: invalid prefix byte")
ErrInvalidKey = nkeysError("nkeys: invalid key")
ErrInvalidPublicKey = nkeysError("nkeys: invalid public key")
ErrInvalidPrivateKey = nkeysError("nkeys: invalid private key")
ErrInvalidSeedLen = nkeysError("nkeys: invalid seed length")
ErrInvalidSeed = nkeysError("nkeys: invalid seed")
ErrInvalidEncoding = nkeysError("nkeys: invalid encoded key")
ErrInvalidSignature = nkeysError("nkeys: signature verification failed")
ErrCannotSign = nkeysError("nkeys: can not sign, no private key available")
ErrPublicKeyOnly = nkeysError("nkeys: no seed or private key available")
ErrIncompatibleKey = nkeysError("nkeys: incompatible key")
ErrInvalidChecksum = nkeysError("nkeys: invalid checksum")
ErrNoSeedFound = nkeysError("nkeys: no nkey seed found")
ErrInvalidNkeySeed = nkeysError("nkeys: doesn't contain a seed nkey")
ErrInvalidUserSeed = nkeysError("nkeys: doesn't contain an user seed nkey")
ErrInvalidRecipient = nkeysError("nkeys: not a valid recipient public curve key")
ErrInvalidSender = nkeysError("nkeys: not a valid sender public curve key")
ErrInvalidCurveKey = nkeysError("nkeys: not a valid curve key")
ErrInvalidCurveSeed = nkeysError("nkeys: not a valid curve seed")
ErrInvalidEncrypted = nkeysError("nkeys: encrypted input is not valid")
ErrInvalidEncVersion = nkeysError("nkeys: encrypted input wrong version")
ErrCouldNotDecrypt = nkeysError("nkeys: could not decrypt input")
ErrInvalidPrefixByte = nkeysError("nkeys: invalid prefix byte")
ErrInvalidKey = nkeysError("nkeys: invalid key")
ErrInvalidPublicKey = nkeysError("nkeys: invalid public key")
ErrInvalidPrivateKey = nkeysError("nkeys: invalid private key")
ErrInvalidSeedLen = nkeysError("nkeys: invalid seed length")
ErrInvalidSeed = nkeysError("nkeys: invalid seed")
ErrInvalidEncoding = nkeysError("nkeys: invalid encoded key")
ErrInvalidSignature = nkeysError("nkeys: signature verification failed")
ErrCannotSign = nkeysError("nkeys: can not sign, no private key available")
ErrPublicKeyOnly = nkeysError("nkeys: no seed or private key available")
ErrIncompatibleKey = nkeysError("nkeys: incompatible key")
ErrInvalidChecksum = nkeysError("nkeys: invalid checksum")
ErrNoSeedFound = nkeysError("nkeys: no nkey seed found")
ErrInvalidNkeySeed = nkeysError("nkeys: doesn't contain a seed nkey")
ErrInvalidUserSeed = nkeysError("nkeys: doesn't contain an user seed nkey")
ErrInvalidRecipient = nkeysError("nkeys: not a valid recipient public curve key")
ErrInvalidSender = nkeysError("nkeys: not a valid sender public curve key")
ErrInvalidCurveKey = nkeysError("nkeys: not a valid curve key")
ErrInvalidCurveSeed = nkeysError("nkeys: not a valid curve seed")
ErrInvalidEncrypted = nkeysError("nkeys: encrypted input is not valid")
ErrInvalidEncVersion = nkeysError("nkeys: encrypted input wrong version")
ErrCouldNotDecrypt = nkeysError("nkeys: could not decrypt input")
ErrInvalidCurveKeyOperation = nkeysError("nkeys: curve key is not valid for sign/verify")
ErrInvalidNKeyOperation = nkeysError("nkeys: only curve key can seal/open")
ErrCannotOpen = nkeysError("nkeys: cannot open no private curve key available")
ErrCannotSeal = nkeysError("nkeys: cannot seal no private curve key available")
)

type nkeysError string
Expand Down
18 changes: 18 additions & 0 deletions keypair.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,9 @@ func CreatePair(prefix PrefixByte) (KeyPair, error) {

// CreatePair will create a KeyPair based on the rand reader and a type/prefix byte. rand can be nil.
func CreatePairWithRand(prefix PrefixByte, rr io.Reader) (KeyPair, error) {
if prefix == PrefixByteCurve {
return CreateCurveKeysWithRand(rr)
}
if rr == nil {
rr = rand.Reader
}
Expand Down Expand Up @@ -126,3 +129,18 @@ func (pair *kp) Verify(input []byte, sig []byte) error {
}
return nil
}

// Seal is only supported on CurveKeyPair
func (pair *kp) Seal(input []byte, recipient string) ([]byte, error) {
return nil, ErrInvalidNKeyOperation
}

// SealWithRand is only supported on CurveKeyPair
func (pair *kp) SealWithRand(input []byte, recipient string, rr io.Reader) ([]byte, error) {
return nil, ErrInvalidNKeyOperation
}

// Open is only supported on CurveKey
func (pair *kp) Open(input []byte, sender string) ([]byte, error) {
return nil, ErrInvalidNKeyOperation
}
15 changes: 14 additions & 1 deletion nkeys.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@
// It also supports encryption via x25519 keys and is compatible with https://pkg.go.dev/golang.org/x/crypto/nacl/box.
package nkeys

import "io"

// Version is our current version
const Version = "0.4.0-beta"

Expand All @@ -24,9 +26,17 @@ type KeyPair interface {
Seed() ([]byte, error)
PublicKey() (string, error)
PrivateKey() ([]byte, error)
// Sign is only supported on Non CurveKeyPairs
Sign(input []byte) ([]byte, error)
// Verify is only supported on Non CurveKeyPairs
Verify(input []byte, sig []byte) error
Wipe()
// Seal is only supported on CurveKeyPair
Seal(input []byte, recipient string) ([]byte, error)
// SealWithRand is only supported on CurveKeyPair
SealWithRand(input []byte, recipient string, rr io.Reader) ([]byte, error)
// Open is only supported on CurveKey
Open(input []byte, sender string) ([]byte, error)
}

// CreateUser will create a User typed KeyPair.
Expand Down Expand Up @@ -69,10 +79,13 @@ func FromPublicKey(public string) (KeyPair, error) {

// FromSeed will create a KeyPair capable of signing and verifying signatures.
func FromSeed(seed []byte) (KeyPair, error) {
_, _, err := DecodeSeed(seed)
prefix, _, err := DecodeSeed(seed)
if err != nil {
return nil, err
}
if prefix == PrefixByteCurve {
return FromCurveSeed(seed)
}
copy := append([]byte{}, seed...)
return &kp{copy}, nil
}
Expand Down
37 changes: 37 additions & 0 deletions nkeys_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -692,3 +692,40 @@ func TestValidateKeyPairRole(t *testing.T) {
}
}
}

func testSealOpen(t *testing.T, prefixByte PrefixByte) {
kp, err := CreatePair(prefixByte)
if err != nil {
t.Fatalf("Failed to create pair for %v", prefixByte)
}
if kp == nil {
t.Fatalf("Failed to create pair for %v - nil keypair", prefixByte)
}
_, err = kp.Open([]byte("hello"), "ME")
if err == nil {
t.Fatalf("Expected Open to fail for %v", prefixByte)
}
if err != ErrInvalidNKeyOperation {
t.Fatalf("Expected Open to fail for %v with %v: %v", prefixByte, ErrInvalidNKeyOperation, err)
}
_, err = kp.Seal([]byte("hello"), "ME")
if err == nil {
t.Fatalf("Expected Seal to fail for %v", prefixByte)
}
if err != ErrInvalidNKeyOperation {
t.Fatalf("Expected Seal to fail for %v with %v: %v", prefixByte, ErrInvalidNKeyOperation, err)
}
_, err = kp.SealWithRand([]byte("hello"), "ME", nil)
if err == nil {
t.Fatalf("Expected SealWithRand to fail for %v", prefixByte)
}
if err != ErrInvalidNKeyOperation {
t.Fatalf("Expected SealWithRand to fail for %v with %v: %v", prefixByte, ErrInvalidNKeyOperation, err)
}
}

func TestSealOpen(t *testing.T) {
testSealOpen(t, PrefixByteOperator)
testSealOpen(t, PrefixByteAccount)
testSealOpen(t, PrefixByteUser)
}
20 changes: 20 additions & 0 deletions public.go
Original file line number Diff line number Diff line change
Expand Up @@ -64,3 +64,23 @@ func (p *pub) Wipe() {
p.pre = '0'
io.ReadFull(rand.Reader, p.pub)
}

func (p *pub) Seal(input []byte, recipient string) ([]byte, error) {
if p.pre == PrefixByteCurve {
return nil, ErrCannotSeal
}
return nil, ErrInvalidNKeyOperation
}
func (p *pub) SealWithRand(input []byte, _recipient string, rr io.Reader) ([]byte, error) {
if p.pre == PrefixByteCurve {
return nil, ErrCannotSeal
}
return nil, ErrInvalidNKeyOperation
}

func (p *pub) Open(input []byte, sender string) ([]byte, error) {
if p.pre == PrefixByteCurve {
return nil, ErrCannotOpen
}
return nil, ErrInvalidNKeyOperation
}
25 changes: 11 additions & 14 deletions xkeys.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,17 +28,6 @@ import (
// We may add more advanced options in the future for group recipients and better
// end to end algorithms.

// CurveKeyPair provides the central interface to xkeys used for encryption.
type CurveKeyPair interface {
Seed() ([]byte, error)
PublicKey() (string, error)
PrivateKey() ([]byte, error)
Seal(input []byte, recipient string) ([]byte, error)
SealWithRand(input []byte, recipient string, rr io.Reader) ([]byte, error)
Open(input []byte, sender string) ([]byte, error)
Wipe()
}

const (
curveKeyLen = 32
curveDecodeLen = 35
Expand All @@ -50,12 +39,12 @@ type ckp struct {
}

// CreateUser will create a User typed KeyPair.
func CreateCurveKeys() (CurveKeyPair, error) {
func CreateCurveKeys() (KeyPair, error) {
return CreateCurveKeysWithRand(rand.Reader)
}

// CreateUser will create a User typed KeyPair with specified rand source.
func CreateCurveKeysWithRand(rr io.Reader) (CurveKeyPair, error) {
func CreateCurveKeysWithRand(rr io.Reader) (KeyPair, error) {
var kp ckp
_, err := io.ReadFull(rr, kp.seed[:])
if err != nil {
Expand All @@ -65,7 +54,7 @@ func CreateCurveKeysWithRand(rr io.Reader) (CurveKeyPair, error) {
}

// Will create a curve key pair from seed.
func FromCurveSeed(seed []byte) (CurveKeyPair, error) {
func FromCurveSeed(seed []byte) (KeyPair, error) {
pb, raw, err := DecodeSeed(seed)
if err != nil {
return nil, err
Expand Down Expand Up @@ -185,3 +174,11 @@ func (pair *ckp) Open(input []byte, sender string) ([]byte, error) {
func (pair *ckp) Wipe() {
io.ReadFull(rand.Reader, pair.seed[:])
}

func (pair *ckp) Sign(_ []byte) ([]byte, error) {
return nil, ErrInvalidCurveKeyOperation
}

func (pair *ckp) Verify(_ []byte, _ []byte) error {
return ErrInvalidCurveKeyOperation
}
81 changes: 71 additions & 10 deletions xkeys_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,15 +19,7 @@ import (
"testing"
)

func TestCurve(t *testing.T) {
kp, err := CreateCurveKeys()
if err != nil {
t.Fatalf("Expected non-nil error on CreateCurveKeys, received %v", err)
}
if kp == nil {
t.Fatal("Expected a non-nil curve key pair")
}

func testCurve(t *testing.T, kp KeyPair) {
// Check seed
seed, err := kp.Seed()
if err != nil {
Expand Down Expand Up @@ -81,6 +73,28 @@ func TestCurve(t *testing.T) {
}
}

func TestCurveFromCreateCurveKeys(t *testing.T) {
kp, err := CreateCurveKeys()
if err != nil {
t.Fatalf("Expected nil error on CreateCurveKeys, received %v", err)
}
if kp == nil {
t.Fatal("Expected a non-nil curve key pair")
}
testCurve(t, kp)
}

func TestCurveFromCreatePair(t *testing.T) {
kp, err := CreatePair(PrefixByteCurve)
if err != nil {
t.Fatalf("Expected nil error on CreatePair, received %v", err)
}
if kp == nil {
t.Fatal("Expected a non-nil curve key pair")
}
testCurve(t, kp)
}

func TestCurveFromSeed(t *testing.T) {
kp, _ := CreateCurveKeys()
seed, _ := kp.Seed()
Expand All @@ -90,6 +104,53 @@ func TestCurveFromSeed(t *testing.T) {
t.Fatalf("Unexpected error deriving curve keypair: %v", err)
}
if !reflect.DeepEqual(kp.(*ckp), nkp.(*ckp)) {
t.Fatalf("Expected the curve pairs to be equal")
t.Fatal("Expected the curve pairs to be equal")
}
testCurve(t, nkp)
}

func TestCurveFromKeyPair(t *testing.T) {
kp, _ := CreatePair(PrefixByteCurve)
_, err := kp.Sign([]byte("hello"))
if err == nil {
t.Fatal("Expected sign to fail as it non supported operation")
}
if err != ErrInvalidCurveKeyOperation {
t.Fatalf("Expected %v but got %v", ErrInvalidCurveKeyOperation, err)
}
err = kp.Verify([]byte("hello"), []byte("bad"))
if err == nil {
t.Fatal("Expected verify to fail as it is unsupported operation")
}
if err != ErrInvalidCurveKeyOperation {
t.Fatalf("Expected %v but got %v", ErrInvalidCurveKeyOperation, err)
}
}

func TestCurvePublic(t *testing.T) {
kp, _ := CreatePair(PrefixByteCurve)
_, err := kp.Sign([]byte("hello"))
if err == nil {
t.Fatal("Expected sign to fail as it non supported operation")
}
pk, err := kp.PublicKey()
if err != nil {
t.Fatalf("Unexpected public key error: %v", err)
}
pub, err := FromPublicKey(pk)
if err != nil {
t.Fatalf("Unexpected error when creating public key: %v", err)
}
_, err = pub.Open([]byte("hello"), "bad")
if err != ErrCannotOpen {
t.Fatalf("Expected %v but got %v", ErrCannotOpen, err)
}
_, err = pub.Seal([]byte("hello"), "bad")
if err != ErrCannotSeal {
t.Fatalf("Expected %v but got %v", ErrCannotSeal, err)
}
_, err = pub.SealWithRand([]byte("hello"), "bad", nil)
if err != ErrCannotSeal {
t.Fatalf("Expected %v but got %v", ErrCannotSeal, err)
}
}

0 comments on commit 47c7408

Please sign in to comment.