-
Notifications
You must be signed in to change notification settings - Fork 2
/
Copy pathkey.go
114 lines (97 loc) · 2.74 KB
/
key.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
package etcdircd
import (
"fmt"
"strings"
"github.com/heyitsanthony/etcdircd/keyencode"
)
const (
keyIdxType int = iota + 1
keyIdxSubtype
keyIdxData
// keyIdxSessions
)
func KeyUserMsg(n string) string { return keyUserMsg(n) }
func keyUserCtl(n string) string { return "/user/ctl/" + n }
func keyUserMsg(n string) string { return "/user/msg/" + n }
func KeyChanMsg(ch string) string { return keyChanMsg(ch) }
func keyChanCtl(ch string) string { return "/chan/ctl/" + ch }
func keyChanMsg(ch string) string { return "/chan/msg/" + ch }
func keyChanNicksPfx(ch string) string { return "/chan/nicks/" + ch + "/" }
func keyChanNicks(ch string, id int64) string { return fmt.Sprintf("/chan/nicks/%s/%x", ch, id) }
func KeyOperCtl(n string) string { return keyOperCtl(n) }
func keyOperCtl(n string) string { return "/oper/ctl/" + n }
func IsChan(ch string) bool { return isChan(ch) }
func isChan(ch string) bool {
// TODO: filter more characers
if len(ch) == 0 {
return false
}
return ch[0] == '#'
}
type keyEncoder struct{ ke keyencode.KeyEncoder }
// NewKeyEncoder creates a key encoder that will encode sensitive parts
// of a given key.
func NewKeyEncoder(ke keyencode.KeyEncoder) keyencode.KeyEncoder {
return &keyEncoder{ke}
}
func splitKey(s string) []string {
ss := strings.Split(s, "/")
if len(ss) <= keyIdxData {
panic(s)
}
// /{chan,user}/{msg,ctl,nicks}/<encrypted>/whatever
switch ss[keyIdxType] {
case "user":
switch ss[keyIdxSubtype] {
case "msg":
case "ctl":
default:
panic(s)
}
case "chan":
switch ss[keyIdxSubtype] {
case "msg":
case "ctl":
case "nicks":
default:
panic(s)
}
case "oper":
// No subtype
default:
panic(s)
}
return ss
}
func (ke *keyEncoder) Encode(s string) string {
ss := splitKey(s)
if len(ss[keyIdxData]) == 0 {
return s
}
// Encode the entire prefix so /a/123 and /b/123 will gives /a/X and /b/Y
// when encrypted.
sse := ke.ke.Encode(strings.Join(ss[:keyIdxData+1], "/"))
// Track length of encoded segment for decode later.
ss[3] = fmt.Sprintf("%d/%s", len(sse), sse)
v := strings.Join(ss, "/")
return v
}
func (ke *keyEncoder) Decode(s string) (string, error) {
ss := splitKey(s)
// Encoded segment's length is in /a/b/<len>
enclen := 0
if n, err := fmt.Sscanf(ss[keyIdxData], "%d", &enclen); n != 1 || err != nil {
panic(err)
}
// Compute the length for prefix /a/b/<len>
pfxlen := len(strings.Join(ss[:keyIdxData+1], "/"))
// Decode encoded segment following the prefix.
dec, err := ke.ke.Decode(s[pfxlen+1 : pfxlen+enclen+1])
if err != nil {
return "", err
}
dec = strings.Split(dec, "/")[keyIdxData]
// Stitch it back together.
v := "/" + ss[keyIdxType] + "/" + ss[keyIdxSubtype] + "/" + dec + s[pfxlen+enclen+1:]
return v, nil
}