From 2aa91a59005c34148d4e1de612c509105c24e4eb Mon Sep 17 00:00:00 2001 From: Stephen Asbury Date: Tue, 20 Nov 2018 14:06:40 -0800 Subject: [PATCH 1/3] Added signing keys to operator claims. --- operator_claims.go | 25 ++++++++++++++++++++++++- operator_claims_test.go | 33 +++++++++++++++++++++++++++++++++ 2 files changed, 57 insertions(+), 1 deletion(-) diff --git a/operator_claims.go b/operator_claims.go index 7498008..279bcc9 100644 --- a/operator_claims.go +++ b/operator_claims.go @@ -8,7 +8,8 @@ import ( // Operator specific claims type Operator struct { - Identities []Identity `json:"identity,omitempty"` + Identities []Identity `json:"identity,omitempty"` + SigningKeys []string `json:"signing_keys,omitempty"` } // Validate checks the validity of the operators contents @@ -16,6 +17,16 @@ func (o *Operator) Validate(vr *ValidationResults) { for _, i := range o.Identities { i.Validate(vr) } + + if o.SigningKeys == nil { + return + } + + for _, k := range o.SigningKeys { + if !nkeys.IsValidPublicOperatorKey([]byte(k)) { + vr.AddError("%s is not an operator public key", k) + } + } } // OperatorClaims define the data for an operator JWT @@ -34,6 +45,18 @@ func NewOperatorClaims(subject string) *OperatorClaims { return c } +// AddSigningKey creates the signing keys array if necessary +// appends the new key, NO Validation is performed +func (s *OperatorClaims) AddSigningKey(pk string) { + + if s.SigningKeys == nil { + s.SigningKeys = []string{pk} + return + } + + s.SigningKeys = append(s.SigningKeys, pk) +} + // Encode the claims into a JWT string func (s *OperatorClaims) Encode(pair nkeys.KeyPair) (string, error) { if !nkeys.IsValidPublicOperatorKey(([]byte(s.Subject))) { diff --git a/operator_claims_test.go b/operator_claims_test.go index 40873b2..e92964f 100644 --- a/operator_claims_test.go +++ b/operator_claims_test.go @@ -112,3 +112,36 @@ func TestOperatorType(t *testing.T) { } } + +func TestSigningKeyValidation(t *testing.T) { + ckp := createOperatorNKey(t) + ckp2 := createOperatorNKey(t) + + uc := NewOperatorClaims(publicKey(ckp, t)) + uc.Expires = time.Now().Add(time.Duration(time.Hour)).Unix() + uc.AddSigningKey(publicKey(ckp2, t)) + uJwt := encode(uc, ckp, t) + + uc2, err := DecodeOperatorClaims(uJwt) + if err != nil { + t.Fatal("failed to decode", err) + } + + AssertEquals(len(uc2.SigningKeys), 1, t) + AssertEquals(uc2.SigningKeys[0] == publicKey(ckp2, t), true, t) + + vr := &ValidationResults{} + uc.Validate(vr) + + if len(vr.Issues) != 0 { + t.Fatal("valid operator key should have no validation issues") + } + + uc.AddSigningKey("") // add an invalid one + + vr = &ValidationResults{} + uc.Validate(vr) + if len(vr.Issues) == 0 { + t.Fatal("bad signing key should be invalid") + } +} From 0c54a69bbf33bf1ded4b3c44097cd6a9c2f527f5 Mon Sep 17 00:00:00 2001 From: Stephen Asbury Date: Tue, 20 Nov 2018 14:36:13 -0800 Subject: [PATCH 2/3] Added DidSign to operatory claims --- operator_claims.go | 25 +++++++++++++++++++++ operator_claims_test.go | 50 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 75 insertions(+) diff --git a/operator_claims.go b/operator_claims.go index 279bcc9..1b6652f 100644 --- a/operator_claims.go +++ b/operator_claims.go @@ -45,6 +45,31 @@ func NewOperatorClaims(subject string) *OperatorClaims { return c } +// DidSign checks the claims against the operator's public key and its signing keys +func (s *OperatorClaims) DidSign(op Claims) bool { + if op == nil { + return false + } + + issuer := op.Claims().Issuer + + if issuer == s.Subject { + return true + } + + if s.SigningKeys == nil { + return false + } + + for _, k := range s.SigningKeys { + if k == issuer { + return true + } + } + + return false +} + // AddSigningKey creates the signing keys array if necessary // appends the new key, NO Validation is performed func (s *OperatorClaims) AddSigningKey(pk string) { diff --git a/operator_claims_test.go b/operator_claims_test.go index e92964f..c18bd6d 100644 --- a/operator_claims_test.go +++ b/operator_claims_test.go @@ -145,3 +145,53 @@ func TestSigningKeyValidation(t *testing.T) { t.Fatal("bad signing key should be invalid") } } + +func TestSignedBy(t *testing.T) { + ckp := createOperatorNKey(t) + ckp2 := createOperatorNKey(t) + + uc := NewOperatorClaims(publicKey(ckp, t)) + uc2 := NewOperatorClaims(publicKey(ckp2, t)) + + akp := createAccountNKey(t) + ac := NewAccountClaims(publicKey(akp, t)) + enc, err := ac.Encode(ckp) // sign with the operator key + if err != nil { + t.Fatal("failed to encode", err) + } + ac, err = DecodeAccountClaims(enc) + if err != nil { + t.Fatal("failed to decode", err) + } + + AssertEquals(uc.DidSign(ac), true, t) + AssertEquals(uc2.DidSign(ac), false, t) + + enc, err = ac.Encode(ckp2) // sign with the other operator key + if err != nil { + t.Fatal("failed to encode", err) + } + ac, err = DecodeAccountClaims(enc) + if err != nil { + t.Fatal("failed to decode", err) + } + + AssertEquals(uc.DidSign(ac), false, t) // no signing key + AssertEquals(uc2.DidSign(ac), true, t) // actual key + uc.AddSigningKey(publicKey(ckp2, t)) + AssertEquals(uc.DidSign(ac), true, t) // signing key + + clusterKey := createClusterNKey(t) + clusterClaims := NewClusterClaims(publicKey(clusterKey, t)) + enc, err = clusterClaims.Encode(ckp2) // sign with the operator key + if err != nil { + t.Fatal("failed to encode", err) + } + clusterClaims, err = DecodeClusterClaims(enc) + if err != nil { + t.Fatal("failed to decode", err) + } + + AssertEquals(uc.DidSign(clusterClaims), true, t) // signing key + AssertEquals(uc2.DidSign(clusterClaims), true, t) // actual key +} From a5bede4cf3f6f9030ff338fdbdbdb9c9f696db8e Mon Sep 17 00:00:00 2001 From: Stephen Asbury Date: Tue, 20 Nov 2018 14:39:19 -0800 Subject: [PATCH 3/3] Removed unneeded nil test --- operator_claims.go | 4 ---- 1 file changed, 4 deletions(-) diff --git a/operator_claims.go b/operator_claims.go index 1b6652f..98430af 100644 --- a/operator_claims.go +++ b/operator_claims.go @@ -57,10 +57,6 @@ func (s *OperatorClaims) DidSign(op Claims) bool { return true } - if s.SigningKeys == nil { - return false - } - for _, k := range s.SigningKeys { if k == issuer { return true