Skip to content

Commit

Permalink
Fixed review comments
Browse files Browse the repository at this point in the history
Renamed ClientKey to KeyMaterial
Renamed SymKey to SymKeyMaterial
Renamed PubKeyMaterialKey to PubKeyMaterial
Added extra validators for Curve25519 and passwords
Fixed README typo
Removed trailing dots to comments
  • Loading branch information
daeMOn63 committed Aug 13, 2019
1 parent b2a1915 commit ae0e7cd
Show file tree
Hide file tree
Showing 17 changed files with 367 additions and 250 deletions.
12 changes: 6 additions & 6 deletions README.md
Expand Up @@ -47,15 +47,15 @@ then you can load your client from disk using the LoadClient helper:
...
```

#### With a PubKeyMaterial key
#### With a PubKeyMaterial

Same as for the symmetric key client, 2 constructors are available:
```go
NewPubKeyClient(id []byte, key ed25519.PrivateKey, filePath string, c2PublicKey [32]byte) (Client, error)
NewPubKeyClientPretty(name string, password string, filePath string, c2PublicKey [32]byte) (Client, error)
NewPubKeyClient(id []byte, key ed25519.PrivateKey, filePath string, c2PubKey []byte) (Client, error)
NewPubKeyClientPretty(name string, password string, filePath string, c2PubKey []byte) (Client, error)
```

accepting the same kind of arguments than the Symmetric Key Client, with the addition of a c2PublicKey one.
accepting the same kind of arguments than the Symmetric Key Client, with the addition of a c2PubKey one.

Remember that in order to unprotect message, the client need to be sent the emitters public keys first.
The password is required to be over 16 characters if used.
Expand All @@ -66,7 +66,7 @@ You should receive messages over MQTT or Kafka using your chosen library the
usual way. Having instantiated an instance of the client, imagine that you
now also have:

var topic String
var topic string
var message []byte

You can then unprotect the message as follows:
Expand Down Expand Up @@ -96,7 +96,7 @@ not returned to users as this may induce security vulnerabilities.

If you wish to transmit a message on a topic, suppose say that you have:

var topic String
var topic string
var message []byte

Then you may simply use the `Protect` function from the client library as
Expand Down
39 changes: 19 additions & 20 deletions client.go
Expand Up @@ -20,13 +20,13 @@ const (
)

var (
// ErrTopicKeyNotFound occurs when a topic key is missing when encryption/decrypting.
// ErrTopicKeyNotFound occurs when a topic key is missing when encryption/decrypting
ErrTopicKeyNotFound = errors.New("topic key not found")
// ErrUnsupportedOperation occurs when trying to manipulate client public keys with a ClientKey not supporting it
ErrUnsupportedOperation = errors.New("this operation is not supported")
)

// Client defines interface for protecting and unprotecting E4 messages and commands.
// Client defines interface for protecting and unprotecting E4 messages and commands
type Client interface {
SetIDKey(key []byte) error

Expand All @@ -43,15 +43,15 @@ type Client interface {
ResetTopics() error
}

// client implements Client interface.
// It holds the client state and is saved to disk for persistent storage.
// client implements Client interface
// It holds the client state and is saved to disk for persistent storage
type client struct {
ID []byte
// TopicKeys maps a topic hash to a key
// (slices []byte can't be map keys, converting to strings)
TopicKeys map[string]keys.TopicKey

Key keys.ClientKey
Key keys.KeyMaterial

FilePath string
ReceivingTopic string
Expand All @@ -71,16 +71,16 @@ func NewSymKeyClient(id []byte, key []byte, filePath string) (Client, error) {
copy(newID, id)
}

symKey, err := keys.NewSymKey(key)
symKeyMaterial, err := keys.NewSymKeyMaterial(key)
if err != nil {
return nil, fmt.Errorf("failed to created symkey from key: %v", err)
}

return newClient(newID, symKey, filePath)
return newClient(newID, symKeyMaterial, filePath)
}

// NewPubKeyClient creates a new client using a ed25519 private key
func NewPubKeyClient(id []byte, key ed25519.PrivateKey, filePath string, c2PublicKey [32]byte) (Client, error) {
func NewPubKeyClient(id []byte, key ed25519.PrivateKey, filePath string, c2PubKey []byte) (Client, error) {
var newID []byte
if len(id) == 0 {
newID = e4crypto.RandomID()
Expand All @@ -89,40 +89,40 @@ func NewPubKeyClient(id []byte, key ed25519.PrivateKey, filePath string, c2Publi
copy(newID, id)
}

pubKeyMaterialKey, err := keys.NewPubKeyMaterialKey(newID, key, c2PublicKey)
pubKeyMaterialKey, err := keys.NewPubKeyMaterial(newID, key, c2PubKey)
if err != nil {
return nil, fmt.Errorf("failed to create ed25519key from key: %v", err)
}

return newClient(newID, pubKeyMaterialKey, filePath)
}

// NewSymKeyClientPretty is like NewClient but takes an client name and a password, rather than raw values.
// NewSymKeyClientPretty is like NewClient but takes an client name and a password, rather than raw values
func NewSymKeyClientPretty(name string, password string, filePath string) (Client, error) {
id := e4crypto.HashIDAlias(name)

key, err := keys.NewSymKeyFromPassword(password)
key, err := keys.NewSymKeyMaterialFromPassword(password)
if err != nil {
return nil, err
}

return newClient(id, key, filePath)
}

// NewPubKeyClientPretty is like NewClient but takes an client name and a password, rather than raw values.
func NewPubKeyClientPretty(name string, password string, filePath string, c2PublicKey [32]byte) (Client, error) {
// NewPubKeyClientPretty is like NewClient but takes an client name and a password, rather than raw values
func NewPubKeyClientPretty(name string, password string, filePath string, c2PubKey []byte) (Client, error) {
id := e4crypto.HashIDAlias(name)

key, err := keys.NewPubKeyMaterialKeyFromPassword(id, password, c2PublicKey)
key, err := keys.NewPubKeyMaterialFromPassword(id, password, c2PubKey)
if err != nil {
return nil, err
}

return newClient(id, key, filePath)
}

// newClient creates a new client, generating a random ID if they are empty.
func newClient(id []byte, clientKey keys.ClientKey, filePath string) (Client, error) {
// newClient creates a new client, generating a random ID if they are empty
func newClient(id []byte, clientKey keys.KeyMaterial, filePath string) (Client, error) {
if len(id) == 0 {
return nil, errors.New("client id must not be empty")
}
Expand All @@ -142,7 +142,7 @@ func newClient(id []byte, clientKey keys.ClientKey, filePath string) (Client, er
return c, nil
}

// LoadClient loads a client state from the file system.
// LoadClient loads a client state from the file system
func LoadClient(filePath string) (Client, error) {
c := &client{}
err := readJSON(filePath, c)
Expand Down Expand Up @@ -228,7 +228,7 @@ func (c *client) UnmarshalJSON(data []byte) error {
return nil
}

// ProtectMessage ..
// ProtectMessage will return protected version of the payload, for the given topic
func (c *client) ProtectMessage(payload []byte, topic string) ([]byte, error) {
topichash := hex.EncodeToString(e4crypto.HashTopic(topic))

Expand Down Expand Up @@ -341,7 +341,6 @@ func (c *client) SetPubKey(key, clientid []byte) error {
return fmt.Errorf("invalid client ID: %v", err)
}

// TODO: validate pubKeys ?
pkStore.AddPubKey(clientid, key)

return c.save()
Expand Down Expand Up @@ -396,7 +395,7 @@ func (c *client) SetIDKey(key []byte) error {
return c.save()
}

// topicForID generate the MQTT topic that a client should subscribe to in order to receive commands.
// topicForID generate the MQTT topic that a client should subscribe to in order to receive commands
func topicForID(id []byte) string {
return idTopicPrefix + hex.EncodeToString(id)
}
42 changes: 28 additions & 14 deletions client_test.go
Expand Up @@ -7,6 +7,7 @@ import (
"encoding/hex"
"reflect"
"testing"
"time"

"gitlab.com/teserakt/e4common/keys"

Expand Down Expand Up @@ -51,7 +52,7 @@ func TestNewClientSymKey(t *testing.T) {
t.Fatalf("topickeys initialized to non-empty array")
}

if _, ok := c1.Key.(keys.SymKey); !ok {
if _, ok := c1.Key.(keys.SymKeyMaterial); !ok {
t.Fatalf("expected client to hold a SymKey, got %T", c1.Key)
}
}
Expand All @@ -68,14 +69,13 @@ func TestProtectUnprotectMessageSymKey(t *testing.T) {

func TestProtectUnprotectMessagePubKey(t *testing.T) {
clientID := e4crypto.RandomID()
var c2Key [32]byte

_, privateKey, err := ed25519.GenerateKey(nil)
if err != nil {
t.Fatalf("Failed to generate ed25519 key: %v", err)
}

client, err := NewPubKeyClient(clientID, privateKey, "./test/data/clienttestprotectPubKey", c2Key)
client, err := NewPubKeyClient(clientID, privateKey, "./test/data/clienttestprotectPubKey", generateCurve25519PubKey(t))
if err != nil {
t.Fatalf("failed to create client: %v", err)
}
Expand Down Expand Up @@ -115,7 +115,7 @@ func testProtectUnprotectMessage(t *testing.T, c Client, protectedConstLength in
t.Fatalf("protected message has invalid length: %v instead of %v", len(protected), protectedlen)
}

// happy case.
// happy case
unprotected, err := c.Unprotect(protected, topic)
if err != nil {
t.Fatalf("unprotect failed: %s", err)
Expand All @@ -138,13 +138,13 @@ func testProtectUnprotectMessage(t *testing.T, c Client, protectedConstLength in

// future timestamp and past timestamp
timestamporig := protected[:e4crypto.TimestampLen]
ts := binary.LittleEndian.Uint64(timestamporig)
tsf := ts + 1000000
tsp := ts - (e4crypto.MaxSecondsDelay + 1)
ts := time.Unix(int64(binary.LittleEndian.Uint64(timestamporig)), 0)
tsf := ts.Add(1000000 * time.Second)
tsp := ts.Add(-(e4crypto.MaxSecondsDelay + 1))
tsfuture := make([]byte, 8)
tspast := make([]byte, 8)
binary.LittleEndian.PutUint64(tsfuture, tsf)
binary.LittleEndian.PutUint64(tspast, tsp)
binary.LittleEndian.PutUint64(tsfuture, uint64(tsf.Unix()))
binary.LittleEndian.PutUint64(tspast, uint64(tsp.Unix()))

futureinvalidprotect := make([]byte, protectedlen)
pastinvalidprotect := make([]byte, protectedlen)
Expand Down Expand Up @@ -248,7 +248,7 @@ func TestProtectUnprotectCommandsPubKey(t *testing.T) {
}

clientID := e4crypto.RandomID()
gc, err := NewPubKeyClient(clientID, clientEdSk, "./test/data/clienttestcommand", c2pk)
gc, err := NewPubKeyClient(clientID, clientEdSk, "./test/data/clienttestcommand", c2pk[:])
if err != nil {
t.Fatalf("failed to create client: %v", err)
}
Expand All @@ -272,8 +272,7 @@ func TestClientPubKeys(t *testing.T) {
t.Run("pubKey client properly add / remove / reset pubKeys", func(t *testing.T) {
clientFilePath := "./test/data/pubclienttestpubkeys"

var c2Key [32]byte
c, err := NewPubKeyClientPretty("testClient", "passwordTestRandom", clientFilePath, c2Key)
c, err := NewPubKeyClientPretty("testClient", "passwordTestRandom", clientFilePath, generateCurve25519PubKey(t))
if err != nil {
t.Fatalf("failed to create client: %v", err)
}
Expand Down Expand Up @@ -354,8 +353,7 @@ func TestClientPubKeys(t *testing.T) {
t.Run("pubKey client return errors on pubKey operations with invalid ids", func(t *testing.T) {
clientFilePath := "./test/data/pubclienttestpubkeys"

var c2Key [32]byte
c, err := NewPubKeyClientPretty("testClient", "passwordTestRandom", clientFilePath, c2Key)
c, err := NewPubKeyClientPretty("testClient", "passwordTestRandom", clientFilePath, generateCurve25519PubKey(t))
if err != nil {
t.Fatalf("failed to create client: %v", err)
}
Expand Down Expand Up @@ -741,3 +739,19 @@ func assertSavedClientPubKeysEquals(t *testing.T, filepath string, c Client) {
t.Fatalf("expected savedClient pubKeys to be %#v, got %#v", cPk, savedPk)
}
}

func generateCurve25519PubKey(t *testing.T) []byte {
var c2PubKey [32]byte

c2EdPubKey, _, err := ed25519.GenerateKey(nil)
if err != nil {
t.Fatalf("Failed to generate c2 public key: %v", err)
}

var c2EdPk [32]byte
copy(c2EdPk[:], c2EdPubKey)

extra25519.PublicKeyToCurve25519(&c2PubKey, &c2EdPk)

return c2PubKey[:]
}
8 changes: 4 additions & 4 deletions commands.go
Expand Up @@ -7,15 +7,15 @@ import (
"golang.org/x/crypto/ed25519"
)

// Command is a command sent by C2 to a client.
// Command is a command sent by C2 to a client
type Command int

// ...
// List of supported commands
const (
RemoveTopic Command = iota
ResetTopics
SetIDKey
SetTopicKey //
SetTopicKey
RemovePubKey
ResetPubKeys
SetPubKey
Expand Down Expand Up @@ -46,7 +46,7 @@ func (c Command) ToByte() byte {
}

// processCommand will attempt to parse given command
// and extract arguments to call expected Client method.
// and extract arguments to call expected Client method
func processCommand(client Client, command []byte) error {
switch Command(command[0]) {
case RemoveTopic:
Expand Down
6 changes: 4 additions & 2 deletions crypto/const.go
@@ -1,14 +1,16 @@
package crypto

// ...
import "time"

// List of global e4 constants
const (
IDLen = 16
KeyLen = 32
TagLen = 16
HashLen = 16
TimestampLen = 8
MaxTopicLen = 512
MaxSecondsDelay = 60 * 10
MaxSecondsDelay = 60 * 10 * time.Second

IDLenHex = IDLen * 2
KeyLenHex = KeyLen * 2
Expand Down

0 comments on commit ae0e7cd

Please sign in to comment.