/
boxed_message.go
104 lines (90 loc) · 2.37 KB
/
boxed_message.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
package crypto
import (
"encoding/base64"
"fmt"
"regexp"
"strconv"
)
var messageParser = regexp.MustCompile("\\AEJ\\[(\\d):([A-Za-z0-9+=/]{44}):([A-Za-z0-9+=/]{32}):(.+)\\]\\z")
// boxedMessage dumps and loads the wire format for encrypted messages. The
// schema is fairly simple:
//
// "EJ["
// SchemaVersion ( "1" )
// ":"
// EncrypterPublic :: base64-encoded 32-byte key
// ":"
// Nonce :: base64-encoded 24-byte nonce
// ":"
// Box :: base64-encoded encrypted message
// "]"
type boxedMessage struct {
SchemaVersion int
EncrypterPublic [32]byte
Nonce [24]byte
Box []byte
}
// IsBoxedMessage tests whether a value is formatted using the boxedMessage
// format. This can be used to determine whether a string value requires
// encryption or is already encrypted.
func IsBoxedMessage(data []byte) bool {
return messageParser.Find(data) != nil
}
// Dump dumps to the wire format
func (b *boxedMessage) Dump() []byte {
pub := base64.StdEncoding.EncodeToString(b.EncrypterPublic[:])
nonce := base64.StdEncoding.EncodeToString(b.Nonce[:])
box := base64.StdEncoding.EncodeToString(b.Box)
str := fmt.Sprintf("EJ[%d:%s:%s:%s]",
b.SchemaVersion, pub, nonce, box)
return []byte(str)
}
// Load restores from the wire format.
func (b *boxedMessage) Load(from []byte) error {
var ssver, spub, snonce, sbox string
var err error
allMatches := messageParser.FindAllStringSubmatch(string(from), -1) // -> [][][]byte
if len(allMatches) != 1 {
return fmt.Errorf("invalid message format")
}
matches := allMatches[0]
if len(matches) != 5 {
return fmt.Errorf("invalid message format")
}
ssver = matches[1]
spub = matches[2]
snonce = matches[3]
sbox = matches[4]
b.SchemaVersion, err = strconv.Atoi(ssver)
if err != nil {
return err
}
pub, err := base64.StdEncoding.DecodeString(spub)
if err != nil {
return err
}
pubBytes := []byte(pub)
if len(pubBytes) != 32 {
return fmt.Errorf("public key invalid")
}
var public [32]byte
copy(public[:], pubBytes[0:32])
b.EncrypterPublic = public
nnc, err := base64.StdEncoding.DecodeString(snonce)
if err != nil {
return err
}
nonceBytes := []byte(nnc)
if len(nonceBytes) != 24 {
return fmt.Errorf("nonce invalid")
}
var nonce [24]byte
copy(nonce[:], nonceBytes[0:24])
b.Nonce = nonce
box, err := base64.StdEncoding.DecodeString(sbox)
if err != nil {
return err
}
b.Box = []byte(box)
return nil
}