Skip to content

Commit

Permalink
Added ability to extract the role of a KeyPair and to validate the ro…
Browse files Browse the repository at this point in the history
…le against a list of roles, as this type of validation is peppered on client code, but should be part of the nkey library.
  • Loading branch information
aricart committed Oct 30, 2019
1 parent 13b630d commit 63f7afc
Show file tree
Hide file tree
Showing 4 changed files with 161 additions and 0 deletions.
2 changes: 2 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
module github.com/nats-io/nkeys

require golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4

go 1.13
1 change: 1 addition & 0 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ var (
ErrInvalidSignature = errors.New("nkeys: signature verification failed")
ErrCannotSign = errors.New("nkeys: can not sign, no private key available")
ErrPublicKeyOnly = errors.New("nkeys: no seed or private key available")
ErrIncompatibleKey = errors.New("nkeys: incompatible key")
)

// KeyPair provides the central interface to nkeys.
Expand Down
104 changes: 104 additions & 0 deletions main_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import (
"encoding/base64"
"io"
"regexp"
"strings"
"testing"

"golang.org/x/crypto/ed25519"
Expand Down Expand Up @@ -618,3 +619,106 @@ func BenchmarkPublicVerify(b *testing.B) {
}
}
}

func TestKeyPairRole(t *testing.T) {
okp, err := CreateOperator()
if err != nil {
t.Fatal(err)
}
akp, err := CreateAccount()
if err != nil {
t.Fatal(err)
}
ukp, err := CreateUser()
if err != nil {
t.Fatal(err)
}
ckp, err := CreateCluster()
if err != nil {
t.Fatal(err)
}
skp, err := CreateServer()
if err != nil {
t.Fatal(err)
}

var keyroles = []struct {
kp KeyPair
pre PrefixByte
name string
}{
{kp: okp, pre: PrefixByteOperator, name: "operator"},
{kp: akp, pre: PrefixByteAccount, name: "account"},
{kp: ukp, pre: PrefixByteUser, name: "user"},
{kp: ckp, pre: PrefixByteCluster, name: "cluster"},
{kp: skp, pre: PrefixByteServer, name: "server"},
}

for _, e := range keyroles {
pre, err := KeyPairRole(e.kp)
if err != nil {
t.Fatalf("%s: %v", e.name, err)
}
if pre != e.pre {
t.Fatalf("expected key to be %v - but was %v", e.pre, pre)
}
}
}

func TestValidateKeyPairRole(t *testing.T) {
okp, err := CreateOperator()
if err != nil {
t.Fatal(err)
}
akp, err := CreateAccount()
if err != nil {
t.Fatal(err)
}
ukp, err := CreateUser()
if err != nil {
t.Fatal(err)
}
ckp, err := CreateCluster()
if err != nil {
t.Fatal(err)
}
skp, err := CreateServer()
if err != nil {
t.Fatal(err)
}

var keyroles = []struct {
kp KeyPair
roles []PrefixByte
ok bool
name string
}{
{kp: okp, name: "want operator", roles: []PrefixByte{PrefixByteOperator}, ok: true},
{kp: akp, name: "want account", roles: []PrefixByte{PrefixByteAccount}, ok: true},
{kp: ukp, name: "want user", roles: []PrefixByte{PrefixByteUser}, ok: true},
{kp: ckp, name: "want cluster", roles: []PrefixByte{PrefixByteCluster}, ok: true},
{kp: skp, name: "want server", roles: []PrefixByte{PrefixByteServer}, ok: true},

{kp: okp, name: "want account or operator", roles: []PrefixByte{PrefixByteOperator, PrefixByteAccount}, ok: true},
{kp: akp, name: "want account or operator", roles: []PrefixByte{PrefixByteOperator, PrefixByteAccount}, ok: true},

{kp: akp, name: "want operator got account", roles: []PrefixByte{PrefixByteOperator}, ok: false},
{kp: ukp, name: "want account or operator got user", roles: []PrefixByte{PrefixByteOperator, PrefixByteAccount}, ok: false},
}

for _, e := range keyroles {
err := ValidateKeyPairRole(e.kp, e.roles...)
if err == nil && !e.ok {
t.Fatalf("test %q should have failed but didn't", e.name)
}
if err != nil && e.ok {
t.Fatalf("test %q should have not failed: %v", e.name, err)

}
if err != nil && !e.ok {
if !strings.Contains(err.Error(), ErrIncompatibleKey.Error()) {
t.Fatalf("unexpected error type for %q: %v", e.name, err)
}
}
}
}
54 changes: 54 additions & 0 deletions strkey.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ import (
"bytes"
"encoding/base32"
"encoding/binary"
"fmt"
"strings"

"golang.org/x/crypto/ed25519"
)
Expand Down Expand Up @@ -289,3 +291,55 @@ func (p PrefixByte) String() string {
}
return "unknown"
}

// Kind returns the PrefixByte for the role of the KeyPair
// That is Operator, Account, User, Cluster, Server or Unknown
func KeyPairRole(kp KeyPair) (PrefixByte, error) {
pk, err := kp.PublicKey()
if err != nil {
return PrefixByteUnknown, err
}

if IsValidPublicOperatorKey(pk) {
return PrefixByteOperator, nil
}

if IsValidPublicAccountKey(pk) {
return PrefixByteAccount, nil
}

if IsValidPublicUserKey(pk) {
return PrefixByteUser, nil
}

if IsValidPublicServerKey(pk) {
return PrefixByteServer, nil
}

if IsValidPublicClusterKey(pk) {
return PrefixByteCluster, nil
}

return PrefixByteUnknown, nil
}

// ValidateKeyPairRole returns an error if the KeyPair doesn't have an expected PrefixByte
func ValidateKeyPairRole(kp KeyPair, expected ...PrefixByte) error {
pkType, err := KeyPairRole(kp)
if err != nil {
return err
}

for _, k := range expected {
if pkType == k {
return nil
}
}

var a []string
for _, e := range expected {
a = append(a, e.String())
}

return fmt.Errorf("%s: %s - expecting %s", ErrIncompatibleKey.Error(), pkType.String(), strings.Join(a, ", "))
}

0 comments on commit 63f7afc

Please sign in to comment.