Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Organizing & simplifying repo along GO standards... #16

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
.idea
72 changes: 6 additions & 66 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,73 +4,13 @@

[![Actions Status](https://github.com/TankerHQ/identity-go/workflows/Tests/badge.svg)](https://github.com/TankerHQ/identity-go/actions) [![codecov](https://codecov.io/gh/TankerHQ/identity-go/branch/master/graph/badge.svg)](https://codecov.io/gh/TankerHQ/identity-go) [![GoDoc][doc-badge]][doc]

Identity generation in Go for the [Tanker SDK](https://docs.tanker.io/latest/).

## Installation

```bash
go get github.com/TankerHQ/identity-go/identity
```
Identity generation in GO for the [Tanker SDK](https://docs.tanker.io/latest/).

## Usage

The server-side code below demonstrates a typical flow to safely deliver identities to your users:

```go
import (
"fmt"
"errors"

"github.com/TankerHQ/identity-go/identity"
)

var config = identity.Config{
AppID: "<app-id>",
AppSecret: "<app-secret>",
}

// Example server-side function in which you would implement checkAuth(),
// retrieveIdentity() and storeIdentity() to use your own authentication
// and data storage mechanisms:
func getIdentity(userID string) (*string, error) {
// Always ensure userID is authenticated before returning a identity
if !isAuthenticated(userID) {
return nil, errors.New("Unauthorized")
}
Add the `github.com/TankerHQ/identity-go` import in your GO file and start using it.

// Retrieve a previously stored identity for this user
identity := retrieveIdentity(userID)

// If not found, create a new identity
if identity == "" {
identity, err := identity.Create(config, userID)
if err != nil {
return nil, err
}

// Store the newly generated identity
storeIdentity(userID, identity)
}

// From now, the same identity will always be returned to a given user
return &identity, nil
}

func getPublicIdentity(userID string) (*string, error) {
// Retrieve a previously stored identity for this user
tkIdentity := retrieveIdentity(userID)
if tkIdentity == "" {
return nil, errors.New("Not found")
}

publicIdentity, err := identity.GetPublicIdentity(tkIdentity)
if err != nil {
return nil, err
}

return publicIdentity, nil
}
```
See the [server-side code from the reference examples](https://pkg.go.dev/github.com/TankerHQ/identity-go#pkg-overview) that demonstrates a typical flow to safely deliver identities to your users.

Read more about identities in the [Tanker guide](https://docs.tanker.io/latest/guides/identity-management/).

Expand All @@ -79,7 +19,7 @@ Read more about identities in the [Tanker guide](https://docs.tanker.io/latest/g
Run tests:

```bash
go test ./... -test.v
go test -v
```

## Contributing
Expand All @@ -88,5 +28,5 @@ Bug reports and pull requests are welcome on GitHub at https://github.com/Tanker

[build-badge]: https://travis-ci.org/TankerHQ/identity-go.svg?branch=master
[build]: https://travis-ci.org/TankerHQ/identity-go
[doc-badge]: https://godoc.org/github.com/TankerHQ/identity-go/identity?status.svg
[doc]: https://godoc.org/github.com/TankerHQ/identity-go/identity
[doc-badge]: https://godoc.org/github.com/TankerHQ/identity-go?status.svg
[doc]: https://godoc.org/github.com/TankerHQ/identity-go
6 changes: 3 additions & 3 deletions b64json/b64json.go → base64.go
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
package b64json
package identity

import (
"encoding/base64"
"encoding/json"
)

func Encode(v interface{}) (*string, error) {
func Base64Encode(v interface{}) (*string, error) {
// Note: []byte values are encoded as base64-encoded strings
// (see: https://golang.org/pkg/encoding/json/#Marshal)
jsonToken, err := json.Marshal(v)
Expand All @@ -17,7 +17,7 @@ func Encode(v interface{}) (*string, error) {
return &b64Token, nil
}

func Decode(b64 string, v interface{}) error {
func Base64Decode(b64 string, v interface{}) error {
str, err := base64.StdEncoding.DecodeString(b64)
if err != nil {
return err
Expand Down
10 changes: 5 additions & 5 deletions identity/config.go → config.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,17 +16,17 @@ type config struct {
AppSecret []byte
}

func (this Config) fromB64() (*config, error) {
appIDBytes, err := base64.StdEncoding.DecodeString(this.AppID)
func (c Config) fromB64() (*config, error) {
appIDBytes, err := base64.StdEncoding.DecodeString(c.AppID)
if err != nil {
return nil, errors.New("Wrong AppID format, should be base64: " + this.AppID)
return nil, errors.New("Wrong AppID format, should be base64: " + c.AppID)
}
if len(appIDBytes) != AppPublicKeySize {
return nil, fmt.Errorf("Expected App ID of size %d, got %d", AppPublicKeySize, len(appIDBytes))
}
appSecretBytes, err := base64.StdEncoding.DecodeString(this.AppSecret)
appSecretBytes, err := base64.StdEncoding.DecodeString(c.AppSecret)
if err != nil {
return nil, errors.New("Wrong AppSecret format, should be base64: " + this.AppSecret)
return nil, errors.New("Wrong AppSecret format, should be base64: " + c.AppSecret)
}
if len(appSecretBytes) != AppSecretSize {
return nil, fmt.Errorf("Expected App secret of size %d, got %d", AppSecretSize, len(appSecretBytes))
Expand Down
23 changes: 0 additions & 23 deletions curve25519/curve25519Generate.go

This file was deleted.

29 changes: 0 additions & 29 deletions curve25519/curve25519Generate_test.go

This file was deleted.

13 changes: 0 additions & 13 deletions curve25519/curve25519_suite_test.go

This file was deleted.

58 changes: 58 additions & 0 deletions example_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
package identity_test

import (
"fmt"
"log"

"github.com/TankerHQ/identity-go"
)

var (
userID = "john123"
config = identity.Config{AppID: "<app-id>", AppSecret: "<app-secret>"}
anyStore = fakeStore{}
)

// Example server-side function in which you would implement your own
// authentication, and a store to persist and retrieve identities:
func ExampleGetIdentity() {
// Always ensure userID is authenticated before returning a identity
if !anyStore.isAuthenticated(userID) {
log.Fatal("unauthorized")
}

// Retrieve a previously stored identity for this user
userIdentity := anyStore.get(userID)

// If not found, create a new identity
if userIdentity == "" {
identity, err := identity.Create(config, userID)
if err != nil {
log.Fatal(err)
}
// Store the newly generated identity
anyStore.persist(userID, *identity)
}
// From now, the same identity will always be returned to a given user
}

func ExampleGetPublicIdentity() {
// Retrieve a previously stored identity for this user
tkIdentity := anyStore.get(userID)
if tkIdentity == "" {
log.Fatal("not found")
}

publicIdentity, err := identity.GetPublicIdentity(tkIdentity)
if err != nil {
log.Fatal(err)
}

fmt.Println(publicIdentity)
}

type fakeStore struct{}

func (fakeStore) isAuthenticated(string) bool { return false }
func (fakeStore) persist(string, string) error { return nil }
func (fakeStore) get(string) string { return "" }
6 changes: 2 additions & 4 deletions identity/identity.go → generate.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,9 @@ package identity

import (
"bytes"
"crypto/ed25519"
"encoding/base64"
"errors"

"github.com/TankerHQ/identity-go/curve25519"
"golang.org/x/crypto/ed25519"
)

type publicIdentity struct {
Expand Down Expand Up @@ -74,7 +72,7 @@ func generateProvisionalIdentity(config config, email string) (*provisionalIdent
if err != nil {
return nil, err
}
publicEncryptionKey, privateEncryptionKey, err := curve25519.GenerateKey()
publicEncryptionKey, privateEncryptionKey, err := GenerateKey()
if err != nil {
return nil, err
}
Expand Down
82 changes: 82 additions & 0 deletions generate_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
package identity

import (
"bytes"
"crypto/ed25519"
"encoding/base64"
"testing"
)

func TestGenerateIdentity(t *testing.T) {
appSecret := decodeBase64string("cTMoGGUKhwN47ypq4xAXAtVkNWeyUtMltQnYwJhxWYSvqjPVGmXd2wwa7y17QtPTZhn8bxb015CZC/e4ZI7+MQ==")
appID := decodeBase64string("tpoxyNzh0hU9G2i9agMvHyyd+pO6zGCjO9BfhrCLjd4=")
conf := config{AppID: appID, AppSecret: appSecret}
userID := "userID"
obfuscatedUserID := hashUserID(appID, userID)

t.Run("returns a valid tanker identity", func(t *testing.T) {
identity, err := generateIdentity(conf, userID)
if err != nil {
t.Fatal(err)
}
if got, want := identity.TrustchainID, appID; !bytes.Equal(got, want) {
t.Fatalf("got %v, want %v", got, want)
}
if got, want := identity.Target, "user"; got != want {
t.Fatalf("got %v, want %v", got, want)
}
if got, want := identity.Value, base64.StdEncoding.EncodeToString(obfuscatedUserID); got != want {
t.Fatalf("got %v, want %v", got, want)
}

expectedTrustChain := decodeBase64string("r6oz1Rpl3dsMGu8te0LT02YZ/G8W9NeQmQv3uGSO/jE=")
checkDelegationSignature(t, *identity, expectedTrustChain)
})

t.Run("returns a valid tanker provisional identity", func(t *testing.T) {
provisionalIdentity, err := generateProvisionalIdentity(conf, "[email protected]")
if err != nil {
t.Fatal(err)
}

if got, want := provisionalIdentity.TrustchainID, appID; !bytes.Equal(got, want) {
t.Fatalf("got %v, want %v", got, want)
}
if got, want := provisionalIdentity.Target, "email"; got != want {
t.Fatalf("got %v, want %v", got, want)
}
if got, want := string(provisionalIdentity.Value), "[email protected]"; got != want {
t.Fatalf("got %v, want %v", got, want)
}
})

t.Run("returns an error if app ID and secret mismatch", func(t *testing.T) {
mismatchingAppID := decodeBase64string("rB0/yEJWCUVYRtDZLtXaJqtneXQOsCSKrtmWw+V+ysc=")
invalidConf := config{AppID: mismatchingAppID, AppSecret: conf.AppSecret}
if _, err := generateIdentity(invalidConf, "[email protected]"); err == nil {
t.Fatal("expected error but got none")
}
})
}

func checkDelegationSignature(t *testing.T, identity identity, trustChainPublicKey []byte) {
t.Helper()

obfuscatedUserID, err := base64.StdEncoding.DecodeString(identity.Value)
if err != nil {
t.Fatal(err)
}
signedData := append(identity.EphemeralPublicSignatureKey, obfuscatedUserID...)

if ed25519.Verify(trustChainPublicKey, signedData, identity.DelegationSignature) == false {
t.Fatal("verification failed")
}
}

func decodeBase64string(s string) []byte {
b, err := base64.StdEncoding.DecodeString(s)
if err != nil {
panic(err)
}
return b
}
6 changes: 1 addition & 5 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,4 @@ module github.com/TankerHQ/identity-go

go 1.12

require (
github.com/onsi/ginkgo v1.8.0
github.com/onsi/gomega v1.5.0
golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4
)
require golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4
Loading