reprogl

Форк
0
/
securecookie.go 
182 строки · 3.2 Кб
1
package session
2

3
import (
4
	"crypto/cipher"
5
	"crypto/des"
6
	"crypto/hmac"
7
	"crypto/rand"
8
	"crypto/sha256"
9
	"crypto/subtle"
10
	"encoding/base64"
11
	"errors"
12
	"hash"
13
)
14

15
type SecureCookie struct {
16
	maxLength int
17
	encoded   string
18
	sz        serializer
19

20
	// For testing purposes
21
	skipExpiration bool
22

23
	hashKey []byte
24
	block   cipher.Block
25
}
26

27
func NewSecureCookie(hashKey, cipherKey string) *SecureCookie {
28
	h := (hashFunc())()
29
	if h.Size() < 24 {
30
		panic(errors.New("session: invalid hash size"))
31
	}
32

33
	h.Write([]byte(cipherKey))
34
	cipherKeyHash := h.Sum(nil)
35
	block, _ := des.NewTripleDESCipher(cipherKeyHash[:24])
36

37
	return &SecureCookie{
38
		maxLength: 4096,
39
		sz:        jsonEncoder{},
40
		hashKey:   []byte(hashKey),
41
		block:     block,
42
	}
43
}
44

45
func (sc *SecureCookie) encode(data internalData) error {
46
	var err error
47
	var b []byte
48

49
	if b, err = sc.sz.serialize(data); err != nil {
50
		return err
51
	}
52

53
	if sc.block != nil {
54
		if b, err = encrypt(sc.block, b); err != nil {
55
			return err
56
		}
57
	}
58

59
	mac := createMac(b, sc.hashKey)
60
	b = append(b, mac...)
61

62
	b = encode(b)
63
	if sc.maxLength != 0 && len(b) > sc.maxLength {
64
		return EncodedValueTooLong
65
	}
66

67
	sc.encoded = string(b)
68

69
	return nil
70
}
71

72
func (sc *SecureCookie) decode(value string) (internalData, error) {
73
	var err error
74
	var b []byte
75
	var data internalData
76

77
	b, err = decode(value)
78
	if err != nil {
79
		return data, err
80
	}
81

82
	if err = verifyMac(b, sc.hashKey); err != nil {
83
		return data, err
84
	}
85

86
	if sc.block != nil {
87
		if b, err = decrypt(sc.block, b); err != nil {
88
			return data, err
89
		}
90
	}
91

92
	data, err = sc.sz.deserialize(b)
93
	if err != nil {
94
		return data, err
95
	}
96

97
	if !sc.skipExpiration && data.deadline.IsExpired() {
98
		return data, Expired
99
	}
100

101
	return data, nil
102
}
103

104
func (*SecureCookie) Name() string {
105
	return CookieName
106
}
107

108
func (*SecureCookie) Path() string {
109
	return "/"
110
}
111

112
func (*SecureCookie) Persist() bool {
113
	return true
114
}
115

116
func (sc *SecureCookie) Value() string {
117
	return sc.encoded
118
}
119

120
func encode(value []byte) []byte {
121
	encoded := make([]byte, base64.URLEncoding.EncodedLen(len(value)))
122
	base64.URLEncoding.Encode(encoded, value)
123

124
	return encoded
125
}
126

127
func decode(value string) ([]byte, error) {
128
	return base64.URLEncoding.DecodeString(value)
129
}
130

131
// createMac creates a message authentication code.
132
func createMac(value, key []byte) []byte {
133
	h := hmac.New(hashFunc(), key)
134
	h.Write(value)
135

136
	return h.Sum(nil)
137
}
138

139
func verifyMac(value, key []byte) error {
140
	h := (hashFunc())()
141
	mac := createMac(value[:len(value)-h.Size()], key)
142
	if subtle.ConstantTimeCompare(value[len(value)-h.Size():], mac) == 1 {
143
		return nil
144
	}
145

146
	return ErrMacInvalid
147
}
148

149
// For testing purposes
150
func (sc *SecureCookie) ignoreExpiration() {
151
	sc.skipExpiration = true
152
}
153

154
func encrypt(block cipher.Block, value []byte) ([]byte, error) {
155
	iv := make([]byte, block.BlockSize())
156
	_, err := rand.Read(iv)
157
	if err != nil {
158
		return nil, EncryptionError
159
	}
160

161
	stream := cipher.NewCTR(block, iv)
162
	stream.XORKeyStream(value, value)
163

164
	return append(iv, value...), nil
165
}
166

167
func decrypt(block cipher.Block, value []byte) ([]byte, error) {
168
	size := block.BlockSize()
169
	if len(value) > size {
170
		iv := value[:size]
171
		value = value[size:]
172
		stream := cipher.NewCTR(block, iv)
173
		stream.XORKeyStream(value, value)
174
		return value, nil
175
	}
176

177
	return nil, DecryptionError
178
}
179

180
func hashFunc() func() hash.Hash {
181
	return sha256.New224
182
}
183

Использование cookies

Мы используем файлы cookie в соответствии с Политикой конфиденциальности и Политикой использования cookies.

Нажимая кнопку «Принимаю», Вы даете АО «СберТех» согласие на обработку Ваших персональных данных в целях совершенствования нашего веб-сайта и Сервиса GitVerse, а также повышения удобства их использования.

Запретить использование cookies Вы можете самостоятельно в настройках Вашего браузера.