Skip to content

Commit

Permalink
vcrypt: add Armor & Unarmor funcs
Browse files Browse the repository at this point in the history
The Armor & Unarmor funcs convert to & from PEM encoded message data.
  • Loading branch information
benburkert committed Dec 6, 2015
1 parent 3e2125b commit 413c73d
Show file tree
Hide file tree
Showing 3 changed files with 140 additions and 0 deletions.
58 changes: 58 additions & 0 deletions armor.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
package vcrypt

import (
"encoding/base64"
"encoding/pem"
"errors"
)

// Armor returns the PEM encoded msg data.
func Armor(msg Message) ([]byte, error) {
env, err := Wrap(msg)
if err != nil {
return nil, err
}

data, err := env.Marshal()
if err != nil {
return nil, err
}

fp, err := msg.Digest()
if err != nil {
return nil, err
}

pemType, err := env.pemType()
if err != nil {
return nil, err
}

p := &pem.Block{
Type: pemType,
Bytes: data,
Headers: map[string]string{
"Digest": base64.StdEncoding.EncodeToString(fp),
},
}

if cmnt := msg.Comment(); cmnt != "" {
p.Headers["Comment"] = cmnt
}

return pem.EncodeToMemory(p), nil
}

// Unarmor constructs a Message from the PEM encoded data.
func Unarmor(data []byte) (Message, []byte, error) {
p, rest := pem.Decode(data)
if p == nil {
return nil, rest, errors.New("invalid armored Message")
}

msg, err := Unmarshal(p.Bytes)
if err != nil {
return nil, rest, err
}
return msg, rest, nil
}
67 changes: 67 additions & 0 deletions armor_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
package vcrypt

import (
"reflect"
"strings"
"testing"
)

func TestArmorRoundTrip(t *testing.T) {
tests := []struct {
msg Message
header string
}{
{
msg: diamondPlan,
header: "-----BEGIN VCRYPT PLAN-----",
},
{
msg: diamondVault.Materials[0],
header: "-----BEGIN VCRYPT MATERIAL-----",
},
{
msg: diamondVault,
header: "-----BEGIN VCRYPT VAULT-----",
},
}

for _, test := range tests {
data, err := Armor(test.msg)
if err != nil {
t.Fatal(err)
}

wfp, err := test.msg.Digest()
if err != nil {
t.Fatal(err)
}

if !strings.Contains(string(data), test.header) {
hdr := strings.Split(string(data), "\n")[0]
t.Errorf("want armor header %q, got %q", test.header, hdr)
}

if cmnt := test.msg.Comment(); cmnt != "" {
if !strings.Contains(string(data), "Comment: "+cmnt+"\n") {
t.Errorf("want Message Comment %q, got none", cmnt)
}
}

got, rest, err := Unarmor(data)
if err != nil {
t.Fatal(err)
}
if len(rest) != 0 {
t.Errorf("unexpected extra armor data: %q", rest)
}

gfp, err := got.Digest()
if err != nil {
t.Fatal(err)
}

if !reflect.DeepEqual(wfp, gfp) {
t.Errorf("want msg.Digest = %v, got %v", wfp, gfp)
}
}
}
15 changes: 15 additions & 0 deletions vcrypt.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,9 @@ type Message interface {

// Unmarshal parses the Message encoded in data.
Unmarshal(data []byte) error

Comment() string
Digest() ([]byte, error)
}

// Wrap returns an intermediate form of the message for marshalling.
Expand Down Expand Up @@ -80,3 +83,15 @@ func Unmarshal(data []byte) (Message, error) {

return env.Message()
}

func (e *Envelope) pemType() (string, error) {
switch {
case e.Plan != nil:
return "VCRYPT PLAN", nil
case e.Material != nil:
return "VCRYPT MATERIAL", nil
case e.Vault != nil:
return "VCRYPT VAULT", nil
}
return "", errors.New("unknown Message type")
}

0 comments on commit 413c73d

Please sign in to comment.