• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

nats-io / nkeys / 152

02 Dec 2022 11:12PM UTC coverage: 69.792% (-12.0%) from 81.818%
152

Pull #37

travis-ci-com

web-flow
Merge 37a3b91d4 into 1269927b7
Pull Request #37: Added in xkeys support, which are encoded x25519 keys.

108 of 108 new or added lines in 3 files covered. (100.0%)

67 of 96 relevant lines covered (69.79%)

1459.08 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

/strkey.go
1
// Copyright 2018-2022 The NATS Authors
2
// Licensed under the Apache License, Version 2.0 (the "License");
3
// you may not use this file except in compliance with the License.
4
// You may obtain a copy of the License at
5
//
6
// http://www.apache.org/licenses/LICENSE-2.0
7
//
8
// Unless required by applicable law or agreed to in writing, software
9
// distributed under the License is distributed on an "AS IS" BASIS,
10
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11
// See the License for the specific language governing permissions and
12
// limitations under the License.
13

14
package nkeys
15

16
import (
17
        "bytes"
18
        "encoding/base32"
19
        "encoding/binary"
20
)
21

22
// PrefixByte is a lead byte representing the type.
23
type PrefixByte byte
24

25
const (
26
        // PrefixByteSeed is the version byte used for encoded NATS Seeds
27
        PrefixByteSeed PrefixByte = 18 << 3 // Base32-encodes to 'S...'
28

29
        // PrefixBytePrivate is the version byte used for encoded NATS Private keys
30
        PrefixBytePrivate PrefixByte = 15 << 3 // Base32-encodes to 'P...'
31

32
        // PrefixByteServer is the version byte used for encoded NATS Servers
33
        PrefixByteServer PrefixByte = 13 << 3 // Base32-encodes to 'N...'
34

35
        // PrefixByteCluster is the version byte used for encoded NATS Clusters
36
        PrefixByteCluster PrefixByte = 2 << 3 // Base32-encodes to 'C...'
37

38
        // PrefixByteOperator is the version byte used for encoded NATS Operators
39
        PrefixByteOperator PrefixByte = 14 << 3 // Base32-encodes to 'O...'
40

41
        // PrefixByteAccount is the version byte used for encoded NATS Accounts
42
        PrefixByteAccount PrefixByte = 0 // Base32-encodes to 'A...'
43

44
        // PrefixByteUser is the version byte used for encoded NATS Users
45
        PrefixByteUser PrefixByte = 20 << 3 // Base32-encodes to 'U...'
46

47
        // PrefixByteCurve is the version byte used for encoded CurveKeys (X25519)
48
        PrefixByteCurve PrefixByte = 23 << 3 // Base32-encodes to 'X...'
49

50
        // PrefixByteUnknown is for unknown prefixes.
51
        PrefixByteUnknown PrefixByte = 25 << 3 // Base32-encodes to 'Z...'
52
)
53

54
// Set our encoding to not include padding '=='
55
var b32Enc = base32.StdEncoding.WithPadding(base32.NoPadding)
56

57
// Encode will encode a raw key or seed with the prefix and crc16 and then base32 encoded.
58
func Encode(prefix PrefixByte, src []byte) ([]byte, error) {
120✔
59
        if err := checkValidPrefixByte(prefix); err != nil {
124✔
60
                return nil, err
4✔
61
        }
4✔
62

63
        var raw bytes.Buffer
116✔
64

116✔
65
        // write prefix byte
116✔
66
        if err := raw.WriteByte(byte(prefix)); err != nil {
116✔
67
                return nil, err
×
68
        }
×
69

70
        // write payload
71
        if _, err := raw.Write(src); err != nil {
116✔
72
                return nil, err
×
73
        }
×
74

75
        // Calculate and write crc16 checksum
76
        err := binary.Write(&raw, binary.LittleEndian, crc16(raw.Bytes()))
116✔
77
        if err != nil {
116✔
78
                return nil, err
×
79
        }
×
80

81
        data := raw.Bytes()
116✔
82
        buf := make([]byte, b32Enc.EncodedLen(len(data)))
116✔
83
        b32Enc.Encode(buf, data)
116✔
84
        return buf[:], nil
116✔
85
}
86

87
// EncodeSeed will encode a raw key with the prefix and then seed prefix and crc16 and then base32 encoded.
88
// `src` must be 32 bytes long (ed25519.SeedSize).
89
func EncodeSeed(public PrefixByte, src []byte) ([]byte, error) {
112✔
90
        if err := checkValidPublicPrefixByte(public); err != nil {
120✔
91
                return nil, err
8✔
92
        }
8✔
93

94
        if len(src) != seedLen {
112✔
95
                return nil, ErrInvalidSeedLen
8✔
96
        }
8✔
97

98
        // In order to make this human printable for both bytes, we need to do a little
99
        // bit manipulation to setup for base32 encoding which takes 5 bits at a time.
100
        b1 := byte(PrefixByteSeed) | (byte(public) >> 5)
96✔
101
        b2 := (byte(public) & 31) << 3 // 31 = 00011111
96✔
102

96✔
103
        var raw bytes.Buffer
96✔
104

96✔
105
        raw.WriteByte(b1)
96✔
106
        raw.WriteByte(b2)
96✔
107

96✔
108
        // write payload
96✔
109
        if _, err := raw.Write(src); err != nil {
96✔
110
                return nil, err
×
111
        }
×
112

113
        // Calculate and write crc16 checksum
114
        err := binary.Write(&raw, binary.LittleEndian, crc16(raw.Bytes()))
96✔
115
        if err != nil {
96✔
116
                return nil, err
×
117
        }
×
118

119
        data := raw.Bytes()
96✔
120
        buf := make([]byte, b32Enc.EncodedLen(len(data)))
96✔
121
        b32Enc.Encode(buf, data)
96✔
122
        return buf, nil
96✔
123
}
124

125
// IsValidEncoding will tell you if the encoding is a valid key.
126
func IsValidEncoding(src []byte) bool {
×
127
        _, err := decode(src)
×
128
        return err == nil
×
129
}
×
130

131
// decode will decode the base32 and check crc16 and the prefix for validity.
132
func decode(src []byte) ([]byte, error) {
332✔
133
        raw := make([]byte, b32Enc.DecodedLen(len(src)))
332✔
134
        n, err := b32Enc.Decode(raw, src)
332✔
135
        if err != nil {
352✔
136
                return nil, err
20✔
137
        }
20✔
138
        raw = raw[:n]
312✔
139

312✔
140
        if len(raw) < 4 {
324✔
141
                return nil, ErrInvalidEncoding
12✔
142
        }
12✔
143

144
        var crc uint16
300✔
145
        checksum := bytes.NewReader(raw[len(raw)-2:])
300✔
146
        if err := binary.Read(checksum, binary.LittleEndian, &crc); err != nil {
300✔
147
                return nil, err
×
148
        }
×
149

150
        // ensure checksum is valid
151
        if err := validate(raw[0:len(raw)-2], crc); err != nil {
328✔
152
                return nil, err
28✔
153
        }
28✔
154

155
        return raw[:len(raw)-2], nil
272✔
156
}
157

158
// Decode will decode the base32 string and check crc16 and enforce the prefix is what is expected.
159
func Decode(expectedPrefix PrefixByte, src []byte) ([]byte, error) {
48✔
160
        if err := checkValidPrefixByte(expectedPrefix); err != nil {
52✔
161
                return nil, err
4✔
162
        }
4✔
163
        raw, err := decode(src)
44✔
164
        if err != nil {
48✔
165
                return nil, err
4✔
166
        }
4✔
167
        b1 := raw[0] & 248 // 248 = 11111000
40✔
168
        if prefix := PrefixByte(b1); prefix != expectedPrefix {
44✔
169
                return nil, ErrInvalidPrefixByte
4✔
170
        }
4✔
171
        return raw[1:], nil
36✔
172
}
173

174
// DecodeSeed will decode the base32 string and check crc16 and enforce the prefix is a seed
175
// and the subsequent type is a valid type.
176
func DecodeSeed(src []byte) (PrefixByte, []byte, error) {
192✔
177
        raw, err := decode(src)
192✔
178
        if err != nil {
220✔
179
                return PrefixByteSeed, nil, err
28✔
180
        }
28✔
181
        // Need to do the reverse here to get back to internal representation.
182
        b1 := raw[0] & 248                          // 248 = 11111000
164✔
183
        b2 := (raw[0]&7)<<5 | ((raw[1] & 248) >> 3) // 7 = 00000111
164✔
184

164✔
185
        if PrefixByte(b1) != PrefixByteSeed {
168✔
186
                return PrefixByteSeed, nil, ErrInvalidSeed
4✔
187
        }
4✔
188
        if checkValidPublicPrefixByte(PrefixByte(b2)) != nil {
160✔
189
                return PrefixByteSeed, nil, ErrInvalidSeed
×
190
        }
×
191
        return PrefixByte(b2), raw[2:], nil
160✔
192
}
193

194
// Prefix returns PrefixBytes of its input
195
func Prefix(src string) PrefixByte {
52✔
196
        b, err := decode([]byte(src))
52✔
197
        if err != nil {
56✔
198
                return PrefixByteUnknown
4✔
199
        }
4✔
200
        prefix := PrefixByte(b[0])
48✔
201
        err = checkValidPrefixByte(prefix)
48✔
202
        if err == nil {
92✔
203
                return prefix
44✔
204
        }
44✔
205
        // Might be a seed.
206
        b1 := b[0] & 248
4✔
207
        if PrefixByte(b1) == PrefixByteSeed {
8✔
208
                return PrefixByteSeed
4✔
209
        }
4✔
210
        return PrefixByteUnknown
×
211
}
212

213
// IsValidPublicKey will decode and verify that the string is a valid encoded public key.
214
func IsValidPublicKey(src string) bool {
16✔
215
        b, err := decode([]byte(src))
16✔
216
        if err != nil {
20✔
217
                return false
4✔
218
        }
4✔
219
        if prefix := PrefixByte(b[0]); checkValidPublicPrefixByte(prefix) != nil {
16✔
220
                return false
4✔
221
        }
4✔
222
        return true
8✔
223
}
224

225
// IsValidPublicUserKey will decode and verify the string is a valid encoded Public User Key.
226
func IsValidPublicUserKey(src string) bool {
4✔
227
        _, err := Decode(PrefixByteUser, []byte(src))
4✔
228
        return err == nil
4✔
229
}
4✔
230

231
// IsValidPublicAccountKey will decode and verify the string is a valid encoded Public Account Key.
232
func IsValidPublicAccountKey(src string) bool {
4✔
233
        _, err := Decode(PrefixByteAccount, []byte(src))
4✔
234
        return err == nil
4✔
235
}
4✔
236

237
// IsValidPublicServerKey will decode and verify the string is a valid encoded Public Server Key.
238
func IsValidPublicServerKey(src string) bool {
4✔
239
        _, err := Decode(PrefixByteServer, []byte(src))
4✔
240
        return err == nil
4✔
241
}
4✔
242

243
// IsValidPublicClusterKey will decode and verify the string is a valid encoded Public Cluster Key.
244
func IsValidPublicClusterKey(src string) bool {
4✔
245
        _, err := Decode(PrefixByteCluster, []byte(src))
4✔
246
        return err == nil
4✔
247
}
4✔
248

249
// IsValidPublicOperatorKey will decode and verify the string is a valid encoded Public Operator Key.
250
func IsValidPublicOperatorKey(src string) bool {
4✔
251
        _, err := Decode(PrefixByteOperator, []byte(src))
4✔
252
        return err == nil
4✔
253
}
4✔
254

255
// IsValidPublicCurveKey will decode and verify the string is a valid encoded Public Curve Key.
256
func IsValidPublicCurveKey(src string) bool {
4✔
257
        _, err := Decode(PrefixByteCurve, []byte(src))
4✔
258
        return err == nil
4✔
259
}
4✔
260

261
// checkValidPrefixByte returns an error if the provided value
262
// is not one of the defined valid prefix byte constants.
263
func checkValidPrefixByte(prefix PrefixByte) error {
216✔
264
        switch prefix {
216✔
265
        case PrefixByteOperator, PrefixByteServer, PrefixByteCluster,
266
                PrefixByteAccount, PrefixByteUser, PrefixByteSeed, PrefixBytePrivate, PrefixByteCurve:
204✔
267
                return nil
204✔
268
        }
269
        return ErrInvalidPrefixByte
12✔
270
}
271

272
// checkValidPublicPrefixByte returns an error if the provided value
273
// is not one of the public defined valid prefix byte constants.
274
func checkValidPublicPrefixByte(prefix PrefixByte) error {
292✔
275
        switch prefix {
292✔
276
        case PrefixByteOperator, PrefixByteServer, PrefixByteCluster, PrefixByteAccount, PrefixByteUser, PrefixByteCurve:
280✔
277
                return nil
280✔
278
        }
279
        return ErrInvalidPrefixByte
12✔
280
}
281

282
func (p PrefixByte) String() string {
×
283
        switch p {
×
284
        case PrefixByteOperator:
×
285
                return "operator"
×
286
        case PrefixByteServer:
×
287
                return "server"
×
288
        case PrefixByteCluster:
×
289
                return "cluster"
×
290
        case PrefixByteAccount:
×
291
                return "account"
×
292
        case PrefixByteUser:
×
293
                return "user"
×
294
        case PrefixByteSeed:
×
295
                return "seed"
×
296
        case PrefixBytePrivate:
×
297
                return "private"
×
298
        case PrefixByteCurve:
×
299
                return "x25519"
×
300
        }
301
        return "unknown"
×
302
}
303

304
// CompatibleKeyPair returns an error if the KeyPair doesn't match expected PrefixByte(s)
305
func CompatibleKeyPair(kp KeyPair, expected ...PrefixByte) error {
36✔
306
        pk, err := kp.PublicKey()
36✔
307
        if err != nil {
36✔
308
                return err
×
309
        }
×
310
        pkType := Prefix(pk)
36✔
311
        for _, k := range expected {
80✔
312
                if pkType == k {
72✔
313
                        return nil
28✔
314
                }
28✔
315
        }
316

317
        return ErrIncompatibleKey
8✔
318
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc