Skip to content

Commit

Permalink
Add Registration Example
Browse files Browse the repository at this point in the history
In addition to the more complete CRUD on data example, add in a dynamic client registration illustration that both demonstrates how to create a new keypair and client as well as the reasoning behind doing so.

These changes also expose the operations that allow for casting arbitrary byte arrays as public/private keys for use in client instantiation after a keypair has been generated.

Fixes E3DB-687
  • Loading branch information
Eric Mann committed Aug 31, 2017
1 parent a69f15c commit ad73e58
Show file tree
Hide file tree
Showing 4 changed files with 116 additions and 9 deletions.
2 changes: 1 addition & 1 deletion client.go
Original file line number Diff line number Diff line change
Expand Up @@ -315,7 +315,7 @@ func (c *Client) getClientKey(ctx context.Context, clientID string) (PublicKey,
return nil, err
}

return makePublicKey(key), nil
return MakePublicKey(key), nil
}

// ReadRaw reads a record given a record ID and returns the record without
Expand Down
8 changes: 4 additions & 4 deletions client_integration_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -76,8 +76,8 @@ func setup() {
ClientEmail: "",
APIKeyID: clientDetails.ApiKeyID,
APISecret: clientDetails.ApiSecret,
PublicKey: makePublicKey(pubBytes),
PrivateKey: makePrivateKey(privBytes),
PublicKey: MakePublicKey(pubBytes),
PrivateKey: MakePrivateKey(privBytes),
APIBaseURL: apiURL,
Logging: false,
}
Expand All @@ -97,8 +97,8 @@ func setup() {
ClientEmail: "",
APIKeyID: shareClientDetails.ApiKeyID,
APISecret: shareClientDetails.ApiSecret,
PublicKey: makePublicKey(pubBytes2),
PrivateKey: makePrivateKey(privBytes2),
PublicKey: MakePublicKey(pubBytes2),
PrivateKey: MakePrivateKey(privBytes2),
APIBaseURL: apiURL,
Logging: false,
}
Expand Down
10 changes: 6 additions & 4 deletions crypto.go
Original file line number Diff line number Diff line change
Expand Up @@ -139,7 +139,8 @@ func GenerateKeyPair() (string, string, error) {
return encodePublicKey(pub), encodePrivateKey(priv), nil
}

func makePublicKey(b []byte) PublicKey {
// MakePublicKey converts a byte array to a PublicKey type
func MakePublicKey(b []byte) PublicKey {
key := [publicKeySize]byte{}
copy(key[:], b)
return &key
Expand All @@ -154,14 +155,15 @@ func decodePublicKey(s string) (PublicKey, error) {
return nil, err
}

return makePublicKey(bytes), nil
return MakePublicKey(bytes), nil
}

func encodePublicKey(k PublicKey) string {
return base64Encode(k[:])
}

func makePrivateKey(b []byte) PrivateKey {
// MakePrivateKey converts a byte array to a PrivateKey type
func MakePrivateKey(b []byte) PrivateKey {
key := [privateKeySize]byte{}
copy(key[:], b)
return &key
Expand All @@ -176,7 +178,7 @@ func decodePrivateKey(s string) (PrivateKey, error) {
return nil, err
}

return makePrivateKey(bytes), nil
return MakePrivateKey(bytes), nil
}

func encodePrivateKey(k PrivateKey) string {
Expand Down
105 changes: 105 additions & 0 deletions registration_example/registration.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
package main

import (
"crypto/rand"
"encoding/base64"
"fmt"
"log"
"os"

"github.com/tozny/e3db-go"
)

// randomSecretKey is otherwise private to the client and is used to generate
// a random string ... it's not actually used for cryptographic operations
// in this example.
func randomSecretKey() *[32]byte {
key := &[32]byte{}
_, err := rand.Read(key[:])
if err != nil {
// we don't expect this to fail
panic("random number generation failed")
}

return key
}

// This program provides a simple example illustrating how to programmatically
// register a client with InnoVault and e3db. In some situations, it's preferable
// to register a client from the server or system that will be using its
// credentials (to ensure that all data is truly encrypted from end-to-end
// with no possibilities of a credential leak). For more detailed information,
// please see the documentation home page: https://tozny.com/documentation/e3db
//
// Author:: Eric Mann (eric@tozny.com)
// Copyright:: Copyright (c) 2017 Tozny, LLC
// License:: Public Domain

func main() {
// A registration token is required to set up a client. In this situation,
// we assume an environment variable called REGISTRATION_TOKEN is set
token := os.Getenv("REGISTRATION_TOKEN")

// Clients can either create new cryptographic keypairs, or load in a pre-defined
// pair of Curve25519 keys. In this situation, we will generate a new keypair.
public_key, private_key, err := e3db.GenerateKeyPair()

fmt.Fprintf(os.Stdout, "Public Key: %s\n", public_key)
fmt.Fprintf(os.Stdout, "Private Key: %s\n", private_key)

// The e3db server keeps track of the name of the curve used with public keys,
// so we need to wrap the generated version with an object helper
wrapped_key := e3db.ClientKey{Curve25519: public_key}

// Clients must be registered with a name unique to your account to help
// differentiate between different sets of credentials in the Admin Console.
// In this example, the name is set at random
client_name := "client_" + base64.RawURLEncoding.EncodeToString(randomSecretKey()[:8])

fmt.Fprintf(os.Stdout, "Client Name: %s\n", client_name)

// Passing all of the data above into the registration routine will create
// a new client with the system. Remember to keep your private key private!
client_info, err := e3db.RegisterClient(token, client_name, wrapped_key, "https://api.e3db.com")
if err != nil {
fmt.Fprintf(os.Stderr, "Unhandled error: %s\n", err)
log.Fatal(err)
}

fmt.Fprintf(os.Stdout, "Client ID: %s\n", client_info.ClientID)
fmt.Fprintf(os.Stdout, "API Key ID: %s\n", client_info.ApiKeyID)
fmt.Fprintf(os.Stdout, "API Secret: %s\n", client_info.ApiSecret)

// ---------------------------------------------------------
// Usage
// ---------------------------------------------------------

// Once the client is registered, you can use it immediately to create the
// configuration used to instantiate a Client that can communicate with
// e3db directly.

pubBytes, _ := base64.RawURLEncoding.DecodeString(public_key)
privBytes, _ := base64.RawURLEncoding.DecodeString(private_key)

config := &e3db.ClientOpts{
ClientID: client_info.ClientID,
ClientEmail: "",
APIKeyID: client_info.ApiKeyID,
APISecret: client_info.ApiSecret,
PublicKey: e3db.MakePublicKey(pubBytes),
PrivateKey: e3db.MakePrivateKey(privBytes),
APIBaseURL: "https://api.e3db.com",
Logging: false,
}

// Now create a client using that configuration.
_, err = e3db.GetClient(*config)
if err != nil {
fmt.Fprintf(os.Stderr, "Unhandled error: %s\n", err)
log.Fatal(err)
}

// From this point on, the new client can be used as any other client to read
// write, delete, and query for records. See the `simple.rb` documentation
// for more complete examples ...
}

0 comments on commit ad73e58

Please sign in to comment.