Coveralls logob
Coveralls logo
  • Home
  • Features
  • Pricing
  • Docs
  • Sign In

mevdschee / aspnetcookie / 4

29 Nov 2016 - 0:14 coverage remained the same at 83.643%
4

push

travis-ci

28af3cff0e6fb491c9e45fad15f89c1b?size=18&default=identiconmevdschee
Add coveralls badge

225 of 269 relevant lines covered (83.64%)

240.58 hits per line

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

83.64
/aspnetcookie.go
1
package aspnetcookie
2

3
import (
4
        "bytes"
5
        "crypto/aes"
6
        "crypto/cipher"
7
        "crypto/hmac"
8
        "crypto/rand"
9
        "crypto/sha1"
10
        "crypto/subtle"
11
        "errors"
12
        "hash"
13
        "io"
14
        "strings"
15
        "time"
16

17
        "unicode/utf16"
18
)
19

20
func newTicket(name string, ttl int64, isPersistent bool, userData, cookiePath string) *FormsAuthenticationTicket {
5×
21
        t := &FormsAuthenticationTicket{
5×
22
                version:           2,
5×
23
                name:              name,
5×
24
                issueDateUtc:      time.Now().UTC().Unix(),
5×
25
                expirationDateUtc: ttl,
5×
26
                isPersistent:      isPersistent,
5×
27
                userData:          userData,
5×
28
                cookiePath:        cookiePath,
5×
29
        }
5×
30
        t.expirationDateUtc = t.expirationDateUtc + t.issueDateUtc
5×
31
        return t
5×
32
}
5×
33

34
func fromUnixTime(v int64) uint64 {
10×
35
        return uint64(v)*10000000 + 621355968000000000
10×
36
}
10×
37

38
func toUnixTime(v uint64) int64 {
20×
39
        return int64((v - 621355968000000000) / 10000000)
20×
40
}
20×
41

42
// FormsAuthenticationTicket holds:
43
type FormsAuthenticationTicket struct {
44
        version           byte
45
        name              string
46
        issueDateUtc      int64
47
        expirationDateUtc int64
48
        isPersistent      bool
49
        userData          string
50
        cookiePath        string
51
}
52

53
func uint64ToBytes(v uint64) []byte {
10×
54
        b := make([]byte, 8)
10×
55
        for p := 0; p < 8; p++ {
90×
56
                b[p] = byte(v & 255)
80×
57
                v = v >> 8
80×
58
        }
80×
59
        return b
10×
60
}
61

62
func uint64FromBytes(b []byte) uint64 {
20×
63
        v := uint64(0)
20×
64
        for p := 0; p < 8; p++ {
180×
65
                v = v << 8
160×
66
                v = v | uint64(b[7-p])
160×
67
        }
160×
68
        return v
20×
69
}
70

71
func boolToByte(v bool) byte {
5×
72
        var b byte
5×
73
        if v {
10×
74
                b = 1
5×
75
        }
5×
76
        return b
5×
77
}
78

79
func boolFromByte(v byte) bool {
10×
80
        var b bool
10×
81
        if v == 0x01 {
20×
82
                b = true
10×
83
        }
10×
84
        return b
10×
85
}
86

87
func intToBytes(v int, b *bytes.Buffer) {
1,535×
88
        if v == 0 {
1,545×
89
                b.WriteByte(byte(v))
10×
90
        } else {
1,535×
91
                for v > 0 {
5,225×
92
                        c := byte(v) & 127
3,700×
93
                        if v > 127 {
5,875×
94
                                c = c | 128
2,175×
95
                        }
2,175×
96
                        b.WriteByte(c)
3,700×
97
                        v = v >> 7
3,700×
98
                }
99
        }
100
}
101

102
func intFromBytes(b *bytes.Buffer) int {
1,545×
103
        var res int
1,545×
104
        var n int
1,545×
105
        var c byte
1,545×
106
        for ok := true; ok; ok = c&128 > 0 {
5,265×
107
                c, _ = b.ReadByte()
3,720×
108
                res = res | (int(c&0x7f) << uint(7*n))
3,720×
109
                n++
3,720×
110
        }
3,720×
111
        return res
1,545×
112
}
113

114
func stringToBytes(s string, b *bytes.Buffer) error {
25×
115
        str := utf16.Encode([]rune(s))
25×
116
        intToBytes(len(str), b)
25×
117
        for _, c := range str {
200×
118
                b.WriteByte(byte(c))
175×
119
                c = c >> 8
175×
120
                b.WriteByte(byte(c))
175×
121
        }
175×
122
        return nil
25×
123
}
124

125
func stringFromBytes(b *bytes.Buffer) (string, error) {
40×
126
        s := intFromBytes(b)
40×
127
        str := make([]uint16, s)
40×
128
        for i := 0; i < s; i++ {
360×
129
                b1, _ := b.ReadByte()
320×
130
                b2, _ := b.ReadByte()
320×
131
                str[i] = uint16(b1) | (uint16(b2) << 8)
320×
132
        }
320×
133
        return string(utf16.Decode(str)), nil
40×
134
}
135

136
// Serialize is
137
func (t *FormsAuthenticationTicket) Serialize() ([]byte, error) {
5×
138
        var err error
5×
139
        var b bytes.Buffer
5×
140
        b.WriteByte(0x01)
5×
141
        b.WriteByte(t.version)
5×
142
        b.Write(uint64ToBytes(fromUnixTime(t.issueDateUtc)))
5×
143
        b.WriteByte(0xfe)
5×
144
        b.Write(uint64ToBytes(fromUnixTime(t.expirationDateUtc)))
5×
145
        b.WriteByte(boolToByte(t.isPersistent))
5×
146
        err = stringToBytes(t.name, &b)
5×
147
        if err != nil {
5×
148
                return nil, err
!
149
        }
!
150
        err = stringToBytes(t.userData, &b)
5×
151
        if err != nil {
5×
152
                return nil, err
!
153
        }
!
154
        err = stringToBytes(t.cookiePath, &b)
5×
155
        if err != nil {
5×
156
                return nil, err
!
157
        }
!
158
        b.WriteByte(0xff)
5×
159
        return b.Bytes(), nil
5×
160
}
161

162
// Deserialize is
163
func (t *FormsAuthenticationTicket) Deserialize(buf []byte) (int, error) {
10×
164
        var err error
10×
165
        b := bytes.NewBuffer(buf)
10×
166
        if c, _ := b.ReadByte(); c != 0x01 {
10×
167
                return len(buf) - b.Len(), errors.New("expected 0x01")
!
168
        }
!
169
        t.version, _ = b.ReadByte()
10×
170
        t.issueDateUtc = toUnixTime(uint64FromBytes(b.Next(8)))
10×
171
        if c, _ := b.ReadByte(); c != 0xfe {
10×
172
                return len(buf) - b.Len(), errors.New("expected 0xfe")
!
173
        }
!
174
        t.expirationDateUtc = toUnixTime(uint64FromBytes(b.Next(8)))
10×
175
        t.isPersistent = boolFromByte(b.Next(1)[0])
10×
176
        if t.name, err = stringFromBytes(b); err != nil {
10×
177
                return len(buf) - b.Len(), err
!
178
        }
!
179
        if t.userData, err = stringFromBytes(b); err != nil {
10×
180
                return len(buf) - b.Len(), err
!
181
        }
!
182
        if t.cookiePath, err = stringFromBytes(b); err != nil {
10×
183
                return len(buf) - b.Len(), err
!
184
        }
!
185
        if c, _ := b.ReadByte(); c != 0xff {
10×
186
                return len(buf) - b.Len(), errors.New("expected 0xff")
!
187
        }
!
188
        return len(buf) - b.Len(), nil
10×
189
}
190

191
// New returns a new AspNetCookie.
192
func New(hashName string, hashKey []byte, blockName string, blockKey []byte) *AspNetCookie {
10×
193
        hashFunctions := map[string]func() hash.Hash{
10×
194
                "SHA1": sha1.New,
10×
195
        }
10×
196
        blockCiphers := map[string]func([]byte) (cipher.Block, error){
10×
197
                "AES": aes.NewCipher,
10×
198
                "NIL": nil,
10×
199
        }
10×
200
        hashFunction, ok := hashFunctions[strings.ToUpper(hashName)]
10×
201
        if !ok {
10×
202
                hashFunction = hashFunctions["SHA1"]
!
203
        }
!
204
        blockCipher, ok := blockCiphers[strings.ToUpper(blockName)]
10×
205
        if !ok || blockKey == nil {
10×
206
                blockCipher = blockCiphers["NIL"]
!
207
        }
!
208
        c := &AspNetCookie{
10×
209
                hashFunc: hashFunction,
10×
210
                hashKey:  hashKey,
10×
211
                block:    blockCipher,
10×
212
                blockKey: blockKey,
10×
213
        }
10×
214
        return c
10×
215
}
216

217
// AspNetCookie encodes and decodes authenticated and encrypted cookie values
218
// holding a FormsAuthenticationTicket.
219
type AspNetCookie struct {
220
        hashKey  []byte
221
        hashFunc func() hash.Hash
222
        blockKey []byte
223
        block    func([]byte) (cipher.Block, error)
224
}
225

226
// EncodeNew encodes a cookie value.
227
func (s *AspNetCookie) EncodeNew(name string, ttl int64, isPersistent bool, userData, cookiePath string) ([]byte, error) {
5×
228
        return s.Encode(newTicket(name, ttl, isPersistent, userData, cookiePath))
5×
229
}
5×
230

231
// Encode encodes a cookie value.
232
func (s *AspNetCookie) Encode(value *FormsAuthenticationTicket) ([]byte, error) {
5×
233
        var err error
5×
234
        var b, rnd []byte
5×
235
        var buf bytes.Buffer
5×
236
        var block cipher.Block
5×
237
        // start with 16 bytes of random data
5×
238
        rnd, _ = GenerateRandomKey(16)
5×
239
        buf.Write(rnd)
5×
240
        // serialize ticket
5×
241
        if b, err = value.Serialize(); err != nil {
5×
242
                return nil, err
!
243
        }
!
244
        // create and append MAC
245
        mac := createMac(hmac.New(s.hashFunc, s.hashKey), b)
5×
246
        b = append(b, mac...)
5×
247
        // encrypt (optional)
5×
248
        if s.block != nil {
10×
249
                // create cipher
5×
250
                block, err = s.block(s.blockKey)
5×
251
                if err != nil {
5×
252
                        return nil, err
!
253
                }
!
254
                // add padding to fill the block
255
                end := ((len(b) + block.BlockSize()) / block.BlockSize()) * block.BlockSize()
5×
256
                pad := end - len(b)
5×
257
                padding := make([]byte, pad)
5×
258
                for i := 0; i < pad; i++ {
55×
259
                        padding[i] = byte(pad)
50×
260
                }
50×
261
                b = append(b, padding...)
5×
262
                // encrypt
5×
263
                if b, err = encrypt(block, b); err != nil {
5×
264
                        return nil, err
!
265
                }
!
266
        }
267
        buf.Write(b)
5×
268
        // add some random data
5×
269
        rnd, _ = GenerateRandomKey(16)
5×
270
        buf.Write(rnd)
5×
271
        // return result
5×
272
        return buf.Bytes(), nil
5×
273
}
274

275
// Decode decodes a cookie value.
276
func (s *AspNetCookie) Decode(b []byte) (*FormsAuthenticationTicket, error) {
10×
277
        var err error
10×
278
        dst := &FormsAuthenticationTicket{}
10×
279
        // remove first and last 16 bytes random data
10×
280
        b = b[16 : len(b)-16]
10×
281
        // decrypt (optional)
10×
282
        if s.block != nil {
20×
283
                block, err := s.block(s.blockKey)
10×
284
                if err != nil {
10×
285
                        return nil, err
!
286
                }
!
287
                end := (len(b) / block.BlockSize()) * block.BlockSize()
10×
288
                if b, err = decrypt(block, b[:end]); err != nil {
10×
289
                        return nil, err
!
290
                }
!
291
        }
292
        // deserialize
293
        var pos int
10×
294
        if pos, err = dst.Deserialize(b); err != nil {
10×
295
                return nil, err
!
296
        }
!
297
        // verify MAC
298
        h := hmac.New(s.hashFunc, s.hashKey)
10×
299
        if err = verifyMac(h, b[0:pos], b[pos:pos+h.Size()]); err != nil {
10×
300
                return nil, err
!
301
        }
!
302
        pos = pos + h.Size()
10×
303
        // verify padding
10×
304
        if s.block != nil {
20×
305
                pad := b[pos]
10×
306
                for i := 0; i < int(pad); i++ {
110×
307
                        if b[pos+i] != pad {
100×
308
                                return nil, errors.New("invalid padding")
!
309
                        }
!
310
                }
311
        }
312
        // return result
313
        return dst, nil
10×
314
}
315

316
// Authentication -------------------------------------------------------------
317

318
// createMac creates a message authentication code (MAC).
319
func createMac(h hash.Hash, value []byte) []byte {
15×
320
        h.Write(value)
15×
321
        return h.Sum(nil)
15×
322
}
15×
323

324
// verifyMac verifies that a message authentication code (MAC) is valid.
325
func verifyMac(h hash.Hash, value []byte, mac []byte) error {
10×
326
        mac2 := createMac(h, value)
10×
327
        // Check that both MACs are of equal length, as subtle.ConstantTimeCompare
10×
328
        // does not do this prior to Go 1.4.
10×
329
        if len(mac) == len(mac2) && subtle.ConstantTimeCompare(mac, mac2) == 1 {
20×
330
                return nil
10×
331
        }
10×
332
        return errors.New("invalid mac")
!
333
}
334

335
// Encryption -----------------------------------------------------------------
336

337
// encrypt encrypts a value using the given block in counter mode.
338
func encrypt(block cipher.Block, value []byte) ([]byte, error) {
5×
339
        iv, err := GenerateRandomKey(block.BlockSize())
5×
340
        if err != nil {
5×
341
                return nil, err
!
342
        }
!
343
        // Encrypt it.
344
        stream := cipher.NewCBCEncrypter(block, iv)
5×
345
        stream.CryptBlocks(value, value)
5×
346
        // Return iv + ciphertext.
5×
347
        return append(iv, value...), nil
5×
348
}
349

350
// decrypt decrypts a value using the given block in counter mode.
351
func decrypt(block cipher.Block, value []byte) ([]byte, error) {
10×
352
        size := block.BlockSize()
10×
353
        if len(value) > size {
20×
354
                // Extract iv.
10×
355
                iv := value[:size]
10×
356
                // Extract ciphertext.
10×
357
                value = value[size:]
10×
358
                // Decrypt it.
10×
359
                stream := cipher.NewCBCDecrypter(block, iv)
10×
360
                stream.CryptBlocks(value, value)
10×
361
                return value, nil
10×
362
        }
10×
363
        return nil, errors.New("decryption failed")
!
364
}
365

366
// Helpers --------------------------------------------------------------------
367

368
// GenerateRandomKey creates a random key with the given length in bytes.
369
func GenerateRandomKey(length int) ([]byte, error) {
15×
370
        k := make([]byte, length)
15×
371
        if _, err := io.ReadFull(rand.Reader, k); err != nil {
15×
372
                return nil, err
!
373
        }
!
374
        return k, nil
15×
375
}
Troubleshooting · Open an Issue · Sales · Support · ENTERPRISE · CAREERS · STATUS
BLOG · TWITTER · Legal & Privacy · Supported CI Services · What's a CI service? · Automated Testing

© 2022 Coveralls, Inc