-
Notifications
You must be signed in to change notification settings - Fork 64
/
frame.go
118 lines (102 loc) · 2.88 KB
/
frame.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
105
106
107
108
109
110
111
112
113
114
115
116
117
118
// Copyright 2015 Keybase, Inc. All rights reserved. Use of
// this source code is governed by the included BSD license.
package saltpack
import (
"regexp"
"strings"
)
type headerOrFooterMarker string
const (
headerMarker headerOrFooterMarker = "BEGIN"
footerMarker headerOrFooterMarker = "END"
maxFrameLength int = 512 // applies to header and footer
maxBrandLength int = 128
)
func pop(v *([]string), n int) (ret []string) {
ret = (*v)[(len(*v) - n):]
*v = (*v)[0:(len(*v) - n)]
return
}
func shift(v *([]string), n int) (ret []string) {
ret = (*v)[0:n]
*v = (*v)[n:]
return
}
func makeFrame(which headerOrFooterMarker, typ MessageType, brand string) string {
sffx := getStringForType(typ)
if len(sffx) == 0 {
return sffx
}
words := []string{string(which)}
if len(brand) > 0 {
words = append(words, brand)
}
words = append(words, strings.ToUpper(FormatName))
words = append(words, sffx)
return strings.Join(words, " ")
}
// MakeArmorHeader makes the armor header for the message type for the given "brand"
func MakeArmorHeader(typ MessageType, brand string) string {
return makeFrame(headerMarker, typ, brand)
}
// MakeArmorFooter makes the armor footer for the message type for the given "brand"
func MakeArmorFooter(typ MessageType, brand string) string {
return makeFrame(footerMarker, typ, brand)
}
func getStringForType(typ MessageType) string {
switch typ {
case MessageTypeEncryption:
return EncryptionArmorString
case MessageTypeAttachedSignature:
return SignedArmorString
case MessageTypeDetachedSignature:
return DetachedSignatureArmorString
default:
return ""
}
}
func parseFrame(m string, typ MessageType, hof headerOrFooterMarker) (brand string, err error) {
if len(m) > maxFrameLength {
err = makeErrBadFrame("Frame is too long")
return
}
// replace blocks of characters in the set [>\n\r\t ] with a single space, so that Go
// can easily parse each piece
re := regexp.MustCompile("[>\n\r\t ]+")
s := strings.TrimSpace(re.ReplaceAllString(m, " "))
sffx := getStringForType(typ)
if len(sffx) == 0 {
err = makeErrBadFrame("Message type %v not found", typ)
return
}
v := strings.Split(s, " ")
if len(v) != 4 && len(v) != 5 {
err = makeErrBadFrame("wrong number of words (%d)", len(v))
return
}
front := shift(&v, 1)
if front[0] != string(hof) {
err = makeErrBadFrame("Bad prefix: %s (wanted %s)", front[0], string(hof))
return
}
expected := getStringForType(typ)
tmp := pop(&v, 2)
received := strings.Join(tmp, " ")
if received != expected {
err = makeErrBadFrame("wanted %q but got %q", expected, received)
return
}
spfn := pop(&v, 1)
if spfn[0] != strings.ToUpper(FormatName) {
err = makeErrBadFrame("bad format name (%s)", spfn[0])
return
}
if len(v) > 0 {
brand = v[0]
if len(brand) > maxBrandLength {
err = makeErrBadFrame("Brand is too long")
return
}
}
return brand, err
}