From c248f84dedfb80d339020d83aff1007447cecf33 Mon Sep 17 00:00:00 2001 From: Matthias Hanel Date: Fri, 29 May 2020 16:15:11 -0400 Subject: [PATCH 1/2] Moved credentials file parsing from jwt to nkey. This allows to avoid unnecessary jwt dependencies. Signed-off-by: Matthias Hanel --- creds_utils.go | 78 +++++++++++++++++++++++++++++++++++++++++++ creds_utils_test.go | 81 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 159 insertions(+) create mode 100644 creds_utils.go create mode 100644 creds_utils_test.go diff --git a/creds_utils.go b/creds_utils.go new file mode 100644 index 0000000..e1c3d94 --- /dev/null +++ b/creds_utils.go @@ -0,0 +1,78 @@ +package nkeys + +import ( + "bytes" + "errors" + "regexp" +) + +var userConfigRE = regexp.MustCompile(`\s*(?:(?:[-]{3,}.*[-]{3,}\r?\n)([\w\-.=]+)(?:\r?\n[-]{3,}.*[-]{3,}\r?\n))`) + +// ParseDecoratedJWT takes a creds file and returns the JWT portion. +func ParseDecoratedJWT(contents []byte) (string, error) { + items := userConfigRE.FindAllSubmatch(contents, -1) + if len(items) == 0 { + return string(contents), nil + } + // First result should be the user JWT. + // We copy here so that if the file contained a seed file too we wipe appropriately. + raw := items[0][1] + tmp := make([]byte, len(raw)) + copy(tmp, raw) + return string(tmp), nil +} + +// ParseDecoratedNKey takes a creds file, finds the NKey portion and creates a +// key pair from it. +func ParseDecoratedNKey(contents []byte) (KeyPair, error) { + var seed []byte + + items := userConfigRE.FindAllSubmatch(contents, -1) + if len(items) > 1 { + seed = items[1][1] + } else { + lines := bytes.Split(contents, []byte("\n")) + for _, line := range lines { + if bytes.HasPrefix(bytes.TrimSpace(line), []byte("SO")) || + bytes.HasPrefix(bytes.TrimSpace(line), []byte("SA")) || + bytes.HasPrefix(bytes.TrimSpace(line), []byte("SU")) { + seed = line + break + } + } + } + if seed == nil { + return nil, errors.New("no nkey seed found") + } + if !bytes.HasPrefix(seed, []byte("SO")) && + !bytes.HasPrefix(seed, []byte("SA")) && + !bytes.HasPrefix(seed, []byte("SU")) { + return nil, errors.New("doesn't contain a seed nkey") + } + kp, err := FromSeed(seed) + if err != nil { + return nil, err + } + return kp, nil +} + +// ParseDecoratedUserNKey takes a creds file, finds the NKey portion and creates a +// key pair from it. Similar to ParseDecoratedNKey but fails for non-user keys. +func ParseDecoratedUserNKey(contents []byte) (KeyPair, error) { + nk, err := ParseDecoratedNKey(contents) + if err != nil { + return nil, err + } + seed, err := nk.Seed() + if err != nil { + return nil, err + } + if !bytes.HasPrefix(seed, []byte("SU")) { + return nil, errors.New("doesn't contain an user seed nkey") + } + kp, err := FromSeed(seed) + if err != nil { + return nil, err + } + return kp, nil +} diff --git a/creds_utils_test.go b/creds_utils_test.go new file mode 100644 index 0000000..dc6e735 --- /dev/null +++ b/creds_utils_test.go @@ -0,0 +1,81 @@ +package nkeys + +import ( + "bytes" + "testing" +) + +func Test_ParseDecoratedJWTBad(t *testing.T) { + v, err := ParseDecoratedJWT([]byte("foo")) + if err != nil { + t.Fatal(err) + } + if v != "foo" { + t.Fatal("unexpected input was not returned") + } +} + +func Test_ParseDecoratedSeedBad(t *testing.T) { + if _, err := ParseDecoratedNKey([]byte("foo")); err == nil { + t.Fatal("Expected error") + } else if err.Error() != "no nkey seed found" { + t.Fatal(err) + } +} + +const ( + credsSeed = `SUAOTBNEUHZDFJT3EUMELT7MQTP24JF3XVCXQNDSCU74G5IU6VAJBKH5LI` + credsJwt = `eyJ0eXAiOiJqd3QiLCJhbGciOiJlZDI1NTE5LW5rZXkifQ.eyJqdGkiOiJHVDROVU5NRUY3Wk1XQ1JCWFZWVURLUVQ2WllQWjc3VzRKUlFYRDNMMjRIS1VKRUNRSDdRIiwiaWF0IjoxNTkwNzgxNTkzLCJpc3MiOiJBQURXTFRISUNWNFNVQUdGNkVLTlZFVzVCQlA3WVJESUJHV0dHSFo1SkJET1FZQTdHVUZNNkFRVSIsIm5hbWUiOiJPUEVSQVRPUiIsInN1YiI6IlVERTZXVEdMVFRQQ1JKUkpDS0JKUkdWTlpUTElWUjdMRUVFTFI0Q1lXV1dCS0pTN1hZSUtYRFVVIiwibmF0cyI6eyJwdWIiOnt9LCJzdWIiOnt9LCJ0eXBlIjoidXNlciIsInZlcnNpb24iOjJ9fQ.c_XQT04wEoVVNDRjPHeKwe17BOrSpQTcftwIbB7KoNEIz6peZCJDc4-J3emVepHofUOWy7IAo9TlLwYhuGHWAQ` + decoratedCreds = `-----BEGIN NATS USER JWT----- +eyJ0eXAiOiJqd3QiLCJhbGciOiJlZDI1NTE5LW5rZXkifQ.eyJqdGkiOiJHVDROVU5NRUY3Wk1XQ1JCWFZWVURLUVQ2WllQWjc3VzRKUlFYRDNMMjRIS1VKRUNRSDdRIiwiaWF0IjoxNTkwNzgxNTkzLCJpc3MiOiJBQURXTFRISUNWNFNVQUdGNkVLTlZFVzVCQlA3WVJESUJHV0dHSFo1SkJET1FZQTdHVUZNNkFRVSIsIm5hbWUiOiJPUEVSQVRPUiIsInN1YiI6IlVERTZXVEdMVFRQQ1JKUkpDS0JKUkdWTlpUTElWUjdMRUVFTFI0Q1lXV1dCS0pTN1hZSUtYRFVVIiwibmF0cyI6eyJwdWIiOnt9LCJzdWIiOnt9LCJ0eXBlIjoidXNlciIsInZlcnNpb24iOjJ9fQ.c_XQT04wEoVVNDRjPHeKwe17BOrSpQTcftwIbB7KoNEIz6peZCJDc4-J3emVepHofUOWy7IAo9TlLwYhuGHWAQ +------END NATS USER JWT------ + +************************* IMPORTANT ************************* +NKEY Seed printed below can be used to sign and prove identity. +NKEYs are sensitive and should be treated as secrets. + +-----BEGIN USER NKEY SEED----- +SUAOTBNEUHZDFJT3EUMELT7MQTP24JF3XVCXQNDSCU74G5IU6VAJBKH5LI +------END USER NKEY SEED------ + +************************************************************* +` +) + +func Test_ParseDecoratedSeedAndJWT(t *testing.T) { + // test with and without \r\n + for _, creds := range [][]byte{[]byte(decoratedCreds), + bytes.ReplaceAll([]byte(decoratedCreds), []byte{'\n'}, []byte{'\r', '\n'})} { + kp, err := ParseDecoratedUserNKey(creds) + if err != nil { + t.Fatal(err) + } + pu, err := kp.Seed() + if err != nil { + t.Fatal(err) + } + if !bytes.Equal(pu, []byte(credsSeed)) { + t.Fatal("seeds don't match") + } + + kp, err = ParseDecoratedNKey(creds) + if err != nil { + t.Fatal(err) + } + pu, err = kp.Seed() + if err != nil { + t.Fatal(err) + } + if !bytes.Equal(pu, []byte(credsSeed)) { + t.Fatal("seeds don't match") + } + + jwt, err := ParseDecoratedJWT(creds) + if err != nil { + t.Fatal(err) + } + if !bytes.Equal([]byte(jwt), []byte(credsJwt)) { + t.Fatal("jwt don't match") + } + } +} From 4341fd26708933084a93e55d512bc0cf6f4491e0 Mon Sep 17 00:00:00 2001 From: Matthias Hanel Date: Fri, 29 May 2020 16:45:55 -0400 Subject: [PATCH 2/2] Moving travis to more recent go versions Signed-off-by: Matthias Hanel --- .travis.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.travis.yml b/.travis.yml index 7df27cb..e1076cd 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,8 +1,8 @@ language: go sudo: false go: -- 1.12.x -- 1.11.x +- 1.14.x +- 1.13.x install: - go get -t ./... @@ -28,4 +28,4 @@ script: # script: curl -sL http://git.io/goreleaser | bash # on: # tags: true -# condition: $TRAVIS_OS_NAME = linux \ No newline at end of file +# condition: $TRAVIS_OS_NAME = linux