-
Notifications
You must be signed in to change notification settings - Fork 62
/
django.go
147 lines (119 loc) 路 4.2 KB
/
django.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
package monster
import (
"bytes"
"encoding/base64"
"fmt"
"strings"
)
type djangoParsedData struct {
data string
timestamp string
signature string
decodedSignature []byte
algorithm string
toBeSigned []byte
compressed bool
parsed bool
}
func (d *djangoParsedData) String() string {
if !d.parsed {
return "Unparsed data"
}
return fmt.Sprintf("Compressed: %t\nData: %s\nTimestamp: %s\nSignature: %s\nAlgorithm: %s\n", d.compressed, d.data, d.timestamp, d.signature, d.algorithm)
}
const (
djangoDecoder = "django"
djangoMinLength = 10
djangoSeparator = `:`
djangoSalt = `django.contrib.sessions.backends.signed_cookiessigner`
)
var (
djangoAlgorithmLength = map[int]string{
20: "sha1",
32: "sha256",
48: "sha384",
64: "sha512",
}
)
func djangoDecode(c *Cookie) bool {
if len(c.raw) < djangoMinLength {
return false
}
rawData := c.raw
var parsedData djangoParsedData
// If the first character is a dot, it's compressed.
if rawData[0] == '.' {
parsedData.compressed = true
rawData = rawData[1:]
}
// Break the cookie out into the session data, timestamp, and signature,
// in that order. Note that we assume the use of `TimestampSigner`.
components := strings.Split(rawData, djangoSeparator)
if len(components) != 3 {
return false
}
parsedData.data = components[0]
parsedData.timestamp = components[1]
parsedData.signature = components[2]
// Django encodes the signature with URL-safe base64
// without padding, so we must use `RawURLEncoding`.
decodedSignature, err := base64.RawURLEncoding.DecodeString(parsedData.signature)
if err != nil {
return false
}
// Determine the algorithm from the digest length, or give up if we can't
// figure it out.
if alg, ok := djangoAlgorithmLength[len(decodedSignature)]; ok {
parsedData.algorithm = alg
} else {
return false
}
parsedData.decodedSignature = decodedSignature
parsedData.toBeSigned = []byte(parsedData.data + djangoSeparator + parsedData.timestamp)
parsedData.parsed = true
c.wasDecodedBy(djangoDecoder, &parsedData)
return true
}
func djangoUnsign(c *Cookie, secret []byte) bool {
// We need to extract `toBeSigned` to prepare what we'll be signing.
parsedData := c.parsedDataFor(djangoDecoder).(*djangoParsedData)
// Derive the correct signature, if this was the correct secret key.
computedSignature := djangoCompute(parsedData.algorithm, secret, parsedData.toBeSigned)
// Compare this signature to the one in the `Cookie`.
return bytes.Compare(parsedData.decodedSignature, computedSignature) == 0
}
func djangoResign(c *Cookie, data string, secret []byte) string {
// We need to extract `toBeSigned` to prepare what we'll be signing.
parsedData := c.parsedDataFor(djangoDecoder).(*djangoParsedData)
// We need to assemble the TBS string with new data.
toBeSigned := base64.RawURLEncoding.EncodeToString([]byte(data)) + djangoSeparator + parsedData.timestamp
// Derive the correct signature, if this was the correct secret key.
computedSignature := djangoCompute(parsedData.algorithm, secret, []byte(toBeSigned))
return toBeSigned + djangoSeparator + base64.RawURLEncoding.EncodeToString(computedSignature)
}
func djangoCompute(algorithm string, secret []byte, data []byte) []byte {
switch algorithm {
case "sha1":
// Django forces us to derive a key for HMAC-ing.
derivedKey := sha1Digest(djangoSalt + string(secret))
// Derive the correct signature, if this was the correct secret key.
return sha1HMAC(derivedKey, data)
case "sha256":
// Django forces us to derive a key for HMAC-ing.
derivedKey := sha256Digest(djangoSalt + string(secret))
// Derive the correct signature, if this was the correct secret key.
return sha256HMAC(derivedKey, data)
case "sha384":
// Django forces us to derive a key for HMAC-ing.
derivedKey := sha384Digest(djangoSalt + string(secret))
// Derive the correct signature, if this was the correct secret key.
return sha384HMAC(derivedKey, data)
case "sha512":
// Django forces us to derive a key for HMAC-ing.
derivedKey := sha512Digest(djangoSalt + string(secret))
// Derive the correct signature, if this was the correct secret key.
return sha512HMAC(derivedKey, data)
default:
panic("unknown algorithm")
}
}