-
Notifications
You must be signed in to change notification settings - Fork 2
/
Copy pathmain.go
163 lines (131 loc) · 4.14 KB
/
main.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
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
package strkey
import (
"bytes"
"encoding/base32"
"encoding/binary"
"github.com/digitalbitsorg/go/crc16"
"github.com/digitalbitsorg/go/support/errors"
)
// ErrInvalidVersionByte is returned when the version byte from a provided
// strkey-encoded string is not one of the valid values.
var ErrInvalidVersionByte = errors.New("invalid version byte")
// VersionByte represents one of the possible prefix values for a StrKey base
// string--the string the when encoded using base32 yields a final StrKey.
type VersionByte byte
const (
//VersionByteAccountID is the version byte used for encoded digitalbits addresses
VersionByteAccountID VersionByte = 6 << 3 // Base32-encodes to 'G...'
//VersionByteSeed is the version byte used for encoded digitalbits seed
VersionByteSeed = 18 << 3 // Base32-encodes to 'S...'
//VersionByteHashTx is the version byte used for encoded digitalbits hashTx
//signer keys.
VersionByteHashTx = 19 << 3 // Base32-encodes to 'T...'
//VersionByteHashX is the version byte used for encoded digitalbits hashX
//signer keys.
VersionByteHashX = 23 << 3 // Base32-encodes to 'X...'
)
// Decode decodes the provided StrKey into a raw value, checking the checksum
// and ensuring the expected VersionByte (the version parameter) is the value
// actually encoded into the provided src string.
func Decode(expected VersionByte, src string) ([]byte, error) {
if err := checkValidVersionByte(expected); err != nil {
return nil, err
}
raw, err := decodeString(src)
if err != nil {
return nil, err
}
// decode into components
version := VersionByte(raw[0])
vp := raw[0 : len(raw)-2]
payload := raw[1 : len(raw)-2]
checksum := raw[len(raw)-2:]
// ensure version byte is expected
if version != expected {
return nil, ErrInvalidVersionByte
}
// ensure checksum is valid
if err := crc16.Validate(vp, checksum); err != nil {
return nil, err
}
// if we made it through the gaunlet, return the decoded value
return payload, nil
}
// MustDecode is like Decode, but panics on error
func MustDecode(expected VersionByte, src string) []byte {
d, err := Decode(expected, src)
if err != nil {
panic(err)
}
return d
}
// Encode encodes the provided data to a StrKey, using the provided version
// byte.
func Encode(version VersionByte, src []byte) (string, error) {
if err := checkValidVersionByte(version); err != nil {
return "", err
}
var raw bytes.Buffer
// write version byte
if err := binary.Write(&raw, binary.LittleEndian, version); err != nil {
return "", err
}
// write payload
if _, err := raw.Write(src); err != nil {
return "", err
}
// calculate and write checksum
checksum := crc16.Checksum(raw.Bytes())
if _, err := raw.Write(checksum); err != nil {
return "", err
}
result := base32.StdEncoding.EncodeToString(raw.Bytes())
return result, nil
}
// MustEncode is like Encode, but panics on error
func MustEncode(version VersionByte, src []byte) string {
e, err := Encode(version, src)
if err != nil {
panic(err)
}
return e
}
// Version extracts and returns the version byte from the provided source
// string.
func Version(src string) (VersionByte, error) {
raw, err := decodeString(src)
if err != nil {
return VersionByte(0), err
}
return VersionByte(raw[0]), nil
}
// checkValidVersionByte returns an error if the provided value
// is not one of the defined valid version byte constants.
func checkValidVersionByte(version VersionByte) error {
if version == VersionByteAccountID {
return nil
}
if version == VersionByteSeed {
return nil
}
if version == VersionByteHashTx {
return nil
}
if version == VersionByteHashX {
return nil
}
return ErrInvalidVersionByte
}
// decodeString decodes a base32 string into the raw bytes, and ensures it could
// potentially be strkey encoded (i.e. it has both a version byte and a
// checksum, neither of which are explicitly checked by this func)
func decodeString(src string) ([]byte, error) {
raw, err := base32.StdEncoding.DecodeString(src)
if err != nil {
return nil, errors.Wrap(err, "base32 decode failed")
}
if len(raw) < 3 {
return nil, errors.Errorf("encoded value is %d bytes; minimum valid length is 3", len(raw))
}
return raw, nil
}