Skip to content

Commit

Permalink
Reworked client structure
Browse files Browse the repository at this point in the history
Added interface definitions for public structs
Moved keys logic to its own package
Replaced client gob encoding by json for better interoperability
Moved crypto things to crypto package
Fixed tests
  • Loading branch information
daeMOn63 committed Aug 8, 2019
1 parent 1643c27 commit 0b7fefc
Show file tree
Hide file tree
Showing 19 changed files with 1,047 additions and 718 deletions.
505 changes: 213 additions & 292 deletions client.go

Large diffs are not rendered by default.

182 changes: 111 additions & 71 deletions client_test.go
Expand Up @@ -4,93 +4,100 @@ import (
"bytes"
"crypto/rand"
"encoding/binary"
"reflect"
"testing"

"gitlab.com/teserakt/e4common/keys"

"github.com/agl/ed25519/extra25519"
"golang.org/x/crypto/ed25519"

e4crypto "gitlab.com/teserakt/e4common/crypto"
)

func TestNewClientSymKey(t *testing.T) {

id := make([]byte, IDLen)
k := make([]byte, KeyLen)
id := make([]byte, e4crypto.IDLen)
k := make([]byte, e4crypto.KeyLen)

rand.Read(id)
rand.Read(k)

_, sk, err := ed25519.GenerateKey(rand.Reader)
path := "./test/data/clienttestnew"
protocol := SymKey

c1, err := NewClient(id, k, sk, path, protocol)

c, err := NewSymKeyClient(id, k, path)
if err != nil {
t.Fatal(err)
}

if c1.ReceivingTopic != TopicForID(id) {
t.Fatalf("receiving topic does not match")
c1, ok := c.(*client)
if !ok {
t.Fatal("failed to cast Client interface to client implementation")
}

if c1.Ed25519Key != nil {
t.Fatalf("ed25519 key should be nil in a SymKey client")
if c1.ReceivingTopic != topicForID(id) {
t.Fatalf("receiving topic does not match")
}

if string(c1.ID) != string(id) {
t.Fatalf("ID does not match")
}

if string(c1.SymKey) != string(k) {
t.Fatalf("symmetric key does not match")
}

if c1.FilePath != path {
t.Fatalf("file path does not match")
}

if len(c1.Pubkeys) != 0 {
t.Fatalf("pubkeys initialized to non-empty array")
if len(c1.TopicKeys) != 0 {
t.Fatalf("topickeys initialized to non-empty array")
}

if len(c1.Topickeys) != 0 {
t.Fatalf("topickeys initialized to non-empty array")
if _, ok := c1.Key.(keys.SymKey); !ok {
t.Fatalf("expected client to hold a SymKey, got %T", c1.Key)
}
}

func TestProtectUnprotectMessageSymKey(t *testing.T) {
testProtectUnprotectMessage(t, SymKey)
}
client, err := NewSymKeyClient(nil, e4crypto.RandomKey(), "./test/data/clienttestprotectSymKey")
if err != nil {
t.Fatalf("failed to create client: %v", err)
}

func TestProtectUnprotectMessagePubKey(t *testing.T) {
testProtectUnprotectMessage(t, PubKey)
protectedConstLength := e4crypto.TagLen + e4crypto.TimestampLen
testProtectUnprotectMessage(t, client, protectedConstLength)
}

func testProtectUnprotectMessage(t *testing.T, protocolVersion Protocol) {

c, err := NewClient(nil, nil, nil, "./test/data/clienttestprotect", protocolVersion)
func TestProtectUnprotectMessagePubKey(t *testing.T) {
clientID := e4crypto.RandomID()
var c2Key [32]byte

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

topic := "topic"

err = c.SetTopicKey(RandomKey(), HashTopic(topic))
client, err := NewPubKeyClient(clientID, privateKey, "./test/data/clienttestprotectPubKey", c2Key)
if err != nil {
t.Fatalf("failed to create client: %v", err)
}

pk := privateKey[32:]
err = client.SetPubKey(pk, clientID)
if err != nil {
t.Fatalf("SetTopicKey failed: %s", err)
t.Fatalf("SetPubKey failed: %s", err)
}

if protocolVersion == PubKey {
pk := c.Ed25519Key[32:]
err = c.SetPubKey(pk, c.ID)
protectedConstLength := e4crypto.TagLen + e4crypto.TimestampLen + e4crypto.IDLen + ed25519.SignatureSize
testProtectUnprotectMessage(t, client, protectedConstLength)
}

if err != nil {
t.Fatalf("SetPubKey failed: %s", err)
}
func testProtectUnprotectMessage(t *testing.T, c Client, protectedConstLength int) {
topic := "topic"
err := c.SetTopicKey(e4crypto.RandomKey(), e4crypto.HashTopic(topic))
if err != nil {
t.Fatalf("SetTopicKey failed: %s", err)
}

for i := 0; i < 2048; i++ {
rdelta := getRDelta()
rdelta := e4crypto.GetRDelta()
msgLen := 123 + int(rdelta)

msg := make([]byte, msgLen)
Expand All @@ -102,11 +109,7 @@ func testProtectUnprotectMessage(t *testing.T, protocolVersion Protocol) {
t.Fatalf("protect failed: %s", err)
}

protectedlen := msgLen + TagLen + TimestampLen
if protocolVersion == PubKey {
protectedlen += IDLen + ed25519.SignatureSize
}

protectedlen := msgLen + protectedConstLength
if len(protected) != protectedlen {
t.Fatalf("protected message has invalid length: %v instead of %v", len(protected), protectedlen)
}
Expand All @@ -133,10 +136,10 @@ func testProtectUnprotectMessage(t *testing.T, protocolVersion Protocol) {
}

// future timestamp and past timestamp
timestamporig := protected[:TimestampLen]
timestamporig := protected[:e4crypto.TimestampLen]
ts := binary.LittleEndian.Uint64(timestamporig)
tsf := ts + 1000000
tsp := ts - (MaxSecondsDelay + 1)
tsp := ts - (e4crypto.MaxSecondsDelay + 1)
tsfuture := make([]byte, 8)
tspast := make([]byte, 8)
binary.LittleEndian.PutUint64(tsfuture, tsf)
Expand All @@ -146,8 +149,8 @@ func testProtectUnprotectMessage(t *testing.T, protocolVersion Protocol) {
pastinvalidprotect := make([]byte, protectedlen)
copy(futureinvalidprotect, tsfuture)
copy(pastinvalidprotect, tspast)
copy(futureinvalidprotect[TimestampLen:], protected[TimestampLen:])
copy(pastinvalidprotect[TimestampLen:], protected[TimestampLen:])
copy(futureinvalidprotect[e4crypto.TimestampLen:], protected[e4crypto.TimestampLen:])
copy(pastinvalidprotect[e4crypto.TimestampLen:], protected[e4crypto.TimestampLen:])

_, err = c.Unprotect(futureinvalidprotect, topic)
if err == nil {
Expand All @@ -165,58 +168,95 @@ func testProtectUnprotectMessage(t *testing.T, protocolVersion Protocol) {
func TestClientWriteRead(t *testing.T) {
filePath := "./test/data/clienttestwriteread"

protocol := SymKey

c, err := NewClient(nil, nil, nil, filePath, protocol)

gc, err := NewSymKeyClient(nil, e4crypto.RandomKey(), filePath)
if err != nil {
t.Fatalf("NewClient failed: %s", err)
t.Fatalf("failed to create client: %v", err)
}

err = c.SetTopicKey(RandomKey(), HashTopic("topic"))
c, ok := gc.(*client)
if !ok {
t.Fatal("failed to cast Client interface to client implementation")
}

err = c.SetTopicKey(e4crypto.RandomKey(), e4crypto.HashTopic("topic"))
if err != nil {
t.Fatalf("SetTopicKey failed: %s", err)
}

err = c.SetIDKey(RandomKey())

err = c.SetIDKey(e4crypto.RandomKey())
if err != nil {
t.Fatalf("SetIDKey failed: %s", err)
}

if len(c.Topickeys) != 1 {
t.Fatalf("invalid number of topic keys: %d vs 1 expected", len(c.Topickeys))
if len(c.TopicKeys) != 1 {
t.Fatalf("invalid number of topic keys: %d vs 1 expected", len(c.TopicKeys))
}

// state should be saved here
err = c.ResetTopics()

if err != nil {
t.Fatalf("save failed: %s", err)
}

cc, err := LoadClient(filePath)
gcc, err := LoadClient(filePath)
if err != nil {
t.Fatalf("client loading failed: %s", err)
}

if string(cc.ID) != string(c.ID) {
t.Fatal("id doesnt match")
}
if string(cc.SymKey) != string(c.SymKey) {
t.Fatal("key doesnt match")
}
if cc.FilePath != c.FilePath {
t.Fatal("filepath doesnt match")
}
// check that topickeys on disk was changed after ResetTopics
if len(cc.Topickeys) != 0 {
t.Fatalf("invalid number of topic keys: %d vs 0 expected", len(cc.Topickeys))
if reflect.DeepEqual(gcc, gc) == false {
t.Fatalf("expected client to be %#v, got %#v", gc, gcc)
}
}

func TestCommands(t *testing.T) {

// TODO
}

func TestProtectUnprotectCommandsPubKey(t *testing.T) {
_, sk, err := ed25519.GenerateKey(rand.Reader)
if err != nil {
t.Fatalf("Failed to generate ed25519 key: %v", err)
}

var cedpk [32]byte
var cpk [32]byte
copy(cedpk[:], sk[32:])
extra25519.PublicKeyToCurve25519(&cpk, &cedpk)

var c2edsk [64]byte
var c2edpk [32]byte
var c2pk [32]byte
var c2sk [32]byte
copy(c2edsk[:], sk)
copy(c2edpk[:], sk[32:])
extra25519.PublicKeyToCurve25519(&c2pk, &c2edpk)
extra25519.PrivateKeyToCurve25519(&c2sk, &c2edsk)

command := []byte{0x05}
protected, err := e4crypto.ProtectCommandPubKey(command, &cpk, &c2sk)

if err != nil {
t.Fatalf("ProtectCommandPubKey failed: %v", err)
}

clientID := e4crypto.RandomID()
gc, err := NewPubKeyClient(clientID, sk, "./test/data/clienttestcommand", c2pk)
if err != nil {
t.Fatalf("failed to create client: %v", err)
}

c, ok := gc.(*client)
if !ok {
t.Fatal("failed to cast Client interface to client implementation")
}

res, err := gc.Unprotect(protected, c.ReceivingTopic)
if err != nil {
t.Fatalf("Unprotect failed: %v", err)
}

if res != nil {
t.Fatalf("Unprotect returned non-nil value")
}
}
58 changes: 58 additions & 0 deletions commands.go
@@ -0,0 +1,58 @@
package e4common

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

// ...
const (
RemoveTopic Command = iota
ResetTopics
SetIDKey
SetTopicKey
RemovePubKey
ResetPubKeys
SetPubKey
)

// ToByte converts a command into its byte representation
func (c *Command) ToByte() byte {
switch *c {
case RemoveTopic:
return 0
case ResetTopics:
return 1
case SetIDKey:
return 2
case SetTopicKey:
return 3
case RemovePubKey:
return 4
case ResetPubKeys:
return 5
case SetPubKey:
return 6
}
return 255
}

// ToString converts a command into its string representation.
func (c *Command) ToString() string {

switch *c {
case RemoveTopic:
return "RemoveTopic"
case ResetTopics:
return "ResetTopics"
case SetIDKey:
return "SetIDKey"
case SetTopicKey:
return "SetTopicKey"
case RemovePubKey:
return "RemovePubKey"
case ResetPubKeys:
return "ResetPubKeys"
case SetPubKey:
return "SetPubKey"
}
return ""
}
18 changes: 18 additions & 0 deletions crypto/const.go
@@ -0,0 +1,18 @@
package crypto

// ...
const (
IDLen = 16
KeyLen = 32
TagLen = 16
HashLen = 16
TimestampLen = 8
MaxTopicLen = 512
MaxSecondsDelay = 60 * 10

IDLenHex = IDLen * 2
KeyLenHex = KeyLen * 2

NameMinLen = 1
NameMaxLen = 255
)

0 comments on commit 0b7fefc

Please sign in to comment.