diff --git a/drbg/seed.go b/drbg/seed.go index cc2a7ec..7c84648 100644 --- a/drbg/seed.go +++ b/drbg/seed.go @@ -1,6 +1,7 @@ package drbg import ( + "bytes" "crypto/rand" "encoding/binary" "encoding/json" @@ -46,14 +47,13 @@ func (s *Seed) MarshalBinary() ([]byte, error) { return s.value[:], nil } -// MarshalJSON serializes the seed to JSON -// Implements the json.Marshaller interface -func (s *Seed) MarshalJSON() ([]byte, error) { +// MarshalText serializes the seed to textual representation +func (s *Seed) MarshalText() ([]byte, error) { return json.Marshal(s.value) } -// UnmarshalJSON restores the seed from a JSON representation. -func (s *Seed) UnmarshalJSON(data []byte) error { +// UnmarshalText restores the seed from a Text representation. +func (s *Seed) UnmarshalText(data []byte) error { if err := json.Unmarshal(data, &s.value); err != nil { return err } @@ -63,6 +63,11 @@ func (s *Seed) UnmarshalJSON(data []byte) error { return nil } +// Equal is used to test equality of two drbg seeds. +func Equal(a, b *Seed) bool { + return bytes.Equal(a.value, b.value) +} + // Key provides the byte representation of the underlying key for the Seed func (s *Seed) Key() []byte { return s.value[:16] diff --git a/libtalek/handle.go b/libtalek/handle.go index 917ef0a..80a74fc 100644 --- a/libtalek/handle.go +++ b/libtalek/handle.go @@ -1,8 +1,10 @@ package libtalek import ( + "bytes" "encoding/binary" "errors" + "fmt" "io" "github.com/agl/ed25519" @@ -184,3 +186,56 @@ func (h *Handle) retrieveResponse(args *common.ReadArgs, reply *common.ReadReply } return nil } + +// MarshalText is a compact textual representation of a handle +func (h *Handle) MarshalText() ([]byte, error) { + s1, err := h.Seed1.MarshalBinary() + if err != nil { + return nil, err + } + s2, err := h.Seed2.MarshalBinary() + if err != nil { + return nil, err + } + txt := fmt.Sprintf("%x.%x.%x.%x.%d", s1, s2, *h.SharedSecret, *h.SigningPublicKey, h.Seqno) + return []byte(txt), nil +} + +// UnmarshalText restores a handle from its compact textual representation +func (h *Handle) UnmarshalText(text []byte) error { + var s1, s2, ss, pk []byte + if n, err := fmt.Sscanf(string(text), "%x.%x.%x.%x.%d", &s1, &s2, &ss, &pk, &h.Seqno); n < 5 || err != nil { + if err != nil { + return err + } + return errors.New("invalid handle") + } + h.SharedSecret = new([32]byte) + copy(h.SharedSecret[:], ss) + h.SigningPublicKey = new([32]byte) + copy(h.SigningPublicKey[:], pk) + h.Seed1 = &drbg.Seed{} + h.Seed2 = &drbg.Seed{} + if err := h.Seed1.UnmarshalBinary(s1); err != nil { + return err + } + if err := h.Seed2.UnmarshalBinary(s2); err != nil { + return err + } + return nil +} + +// Equal tests equality of two handles +func Equal(a, b *Handle) bool { + if a.Seqno != b.Seqno { + return false + } + if !bytes.Equal(a.SharedSecret[:], b.SharedSecret[:]) || + !bytes.Equal(a.SigningPublicKey[:], b.SigningPublicKey[:]) { + return false + } + if (drbg.Equal(a.Seed1, b.Seed1) && drbg.Equal(a.Seed2, b.Seed2)) || (drbg.Equal(a.Seed1, b.Seed2) && drbg.Equal(a.Seed2, b.Seed1)) { + return true + } + return false +} diff --git a/libtalek/handle_test.go b/libtalek/handle_test.go index 0601555..4082a94 100644 --- a/libtalek/handle_test.go +++ b/libtalek/handle_test.go @@ -39,6 +39,26 @@ func TestGeneratePoll(t *testing.T) { fmt.Printf("... done \n") } +func TestSerialization(t *testing.T) { + topic, _ := NewTopic() + h := topic.Handle + + txt, err := h.MarshalText() + if err != nil { + t.Fatalf("Error serializing: %v\n", err) + } + fmt.Printf("Serialized handle looks like %s\n", txt) + + h2, _ := NewHandle() + err = h2.UnmarshalText(txt) + if err != nil { + t.Fatalf("Could not deserialize: %v\n", err) + } + if !Equal(&h, h2) { + t.Fatalf("Serialization lost info!") + } +} + func BenchmarkGeneratePollN10K(b *testing.B) { HelperBenchmarkGeneratePoll(b, 10000/4) } diff --git a/libtalek/topic.go b/libtalek/topic.go index 7625575..ddd003b 100644 --- a/libtalek/topic.go +++ b/libtalek/topic.go @@ -1,8 +1,11 @@ package libtalek import ( + "bytes" "crypto/rand" "encoding/binary" + "errors" + "fmt" "github.com/agl/ed25519" "github.com/privacylab/talek/common" @@ -111,3 +114,29 @@ func (t *Topic) encrypt(plaintext []byte, nonce *[24]byte) ([]byte, error) { digest := ed25519.Sign(t.SigningPrivateKey, buf) return append(buf, digest[:]...), nil } + +// MarshalText is a compact textual representation of a topic +func (t *Topic) MarshalText() ([]byte, error) { + handle, err := t.Handle.MarshalText() + if err != nil { + return nil, err + } + txt := fmt.Sprintf("%x.", *t.SigningPrivateKey) + return append([]byte(txt), handle...), nil +} + +// UnmarshalText restores a topic from its compact textual representation +func (t *Topic) UnmarshalText(text []byte) error { + parts := bytes.SplitN(text, []byte("."), 2) + if len(parts) != 2 { + return errors.New("unparsable topic representation") + } + t.SigningPrivateKey = new([64]byte) + var spk []byte + _, err := fmt.Sscanf(string(parts[0]), "%x", &spk) + if err != nil { + return err + } + copy(t.SigningPrivateKey[:], spk) + return t.Handle.UnmarshalText(parts[1]) +} diff --git a/libtalek/topic_test.go b/libtalek/topic_test.go index af77ea8..388b560 100644 --- a/libtalek/topic_test.go +++ b/libtalek/topic_test.go @@ -48,6 +48,8 @@ func TestSerializeRestore(t *testing.T) { if err != nil { t.Fatalf("Error creating topic: %v\n", err) } + + // test binary encoder var network bytes.Buffer enc := gob.NewEncoder(&network) err = enc.Encode(topic) @@ -60,6 +62,22 @@ func TestSerializeRestore(t *testing.T) { if err != nil { t.Fatalf("Unable to restore topic: %v\n", err) } + + // test text encoder + txt, err := topic.MarshalText() + if err != nil { + t.Fatalf("Error serializing: %v\n", err) + } + fmt.Printf("Serialized topic looks like %s\n", txt) + + clone = Topic{} + err = clone.UnmarshalText(txt) + if err != nil { + t.Fatalf("Could not deserialize: %v\n", err) + } + if !bytes.Equal(topic.SigningPrivateKey[:], clone.SigningPrivateKey[:]) || !Equal(&topic.Handle, &clone.Handle) { + t.Fatalf("serialization lost info!") + } } func TestGeneratePublish(t *testing.T) {