github.com/astaxie/beego@v1.12.3/session/sess_utils.go (about) 1 // Copyright 2014 beego Author. All Rights Reserved. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package session 16 17 import ( 18 "bytes" 19 "crypto/cipher" 20 "crypto/hmac" 21 "crypto/rand" 22 "crypto/sha256" 23 "crypto/subtle" 24 "encoding/base64" 25 "encoding/gob" 26 "errors" 27 "fmt" 28 "io" 29 "strconv" 30 "time" 31 32 "github.com/astaxie/beego/utils" 33 ) 34 35 func init() { 36 gob.Register([]interface{}{}) 37 gob.Register(map[int]interface{}{}) 38 gob.Register(map[string]interface{}{}) 39 gob.Register(map[interface{}]interface{}{}) 40 gob.Register(map[string]string{}) 41 gob.Register(map[int]string{}) 42 gob.Register(map[int]int{}) 43 gob.Register(map[int]int64{}) 44 } 45 46 // EncodeGob encode the obj to gob 47 func EncodeGob(obj map[interface{}]interface{}) ([]byte, error) { 48 for _, v := range obj { 49 gob.Register(v) 50 } 51 buf := bytes.NewBuffer(nil) 52 enc := gob.NewEncoder(buf) 53 err := enc.Encode(obj) 54 if err != nil { 55 return []byte(""), err 56 } 57 return buf.Bytes(), nil 58 } 59 60 // DecodeGob decode data to map 61 func DecodeGob(encoded []byte) (map[interface{}]interface{}, error) { 62 buf := bytes.NewBuffer(encoded) 63 dec := gob.NewDecoder(buf) 64 var out map[interface{}]interface{} 65 err := dec.Decode(&out) 66 if err != nil { 67 return nil, err 68 } 69 return out, nil 70 } 71 72 // generateRandomKey creates a random key with the given strength. 73 func generateRandomKey(strength int) []byte { 74 k := make([]byte, strength) 75 if n, err := io.ReadFull(rand.Reader, k); n != strength || err != nil { 76 return utils.RandomCreateBytes(strength) 77 } 78 return k 79 } 80 81 // Encryption ----------------------------------------------------------------- 82 83 // encrypt encrypts a value using the given block in counter mode. 84 // 85 // A random initialization vector (http://goo.gl/zF67k) with the length of the 86 // block size is prepended to the resulting ciphertext. 87 func encrypt(block cipher.Block, value []byte) ([]byte, error) { 88 iv := generateRandomKey(block.BlockSize()) 89 if iv == nil { 90 return nil, errors.New("encrypt: failed to generate random iv") 91 } 92 // Encrypt it. 93 stream := cipher.NewCTR(block, iv) 94 stream.XORKeyStream(value, value) 95 // Return iv + ciphertext. 96 return append(iv, value...), nil 97 } 98 99 // decrypt decrypts a value using the given block in counter mode. 100 // 101 // The value to be decrypted must be prepended by a initialization vector 102 // (http://goo.gl/zF67k) with the length of the block size. 103 func decrypt(block cipher.Block, value []byte) ([]byte, error) { 104 size := block.BlockSize() 105 if len(value) > size { 106 // Extract iv. 107 iv := value[:size] 108 // Extract ciphertext. 109 value = value[size:] 110 // Decrypt it. 111 stream := cipher.NewCTR(block, iv) 112 stream.XORKeyStream(value, value) 113 return value, nil 114 } 115 return nil, errors.New("decrypt: the value could not be decrypted") 116 } 117 118 func encodeCookie(block cipher.Block, hashKey, name string, value map[interface{}]interface{}) (string, error) { 119 var err error 120 var b []byte 121 // 1. EncodeGob. 122 if b, err = EncodeGob(value); err != nil { 123 return "", err 124 } 125 // 2. Encrypt (optional). 126 if b, err = encrypt(block, b); err != nil { 127 return "", err 128 } 129 b = encode(b) 130 // 3. Create MAC for "name|date|value". Extra pipe to be used later. 131 b = []byte(fmt.Sprintf("%s|%d|%s|", name, time.Now().UTC().Unix(), b)) 132 h := hmac.New(sha256.New, []byte(hashKey)) 133 h.Write(b) 134 sig := h.Sum(nil) 135 // Append mac, remove name. 136 b = append(b, sig...)[len(name)+1:] 137 // 4. Encode to base64. 138 b = encode(b) 139 // Done. 140 return string(b), nil 141 } 142 143 func decodeCookie(block cipher.Block, hashKey, name, value string, gcmaxlifetime int64) (map[interface{}]interface{}, error) { 144 // 1. Decode from base64. 145 b, err := decode([]byte(value)) 146 if err != nil { 147 return nil, err 148 } 149 // 2. Verify MAC. Value is "date|value|mac". 150 parts := bytes.SplitN(b, []byte("|"), 3) 151 if len(parts) != 3 { 152 return nil, errors.New("Decode: invalid value format") 153 } 154 155 b = append([]byte(name+"|"), b[:len(b)-len(parts[2])]...) 156 h := hmac.New(sha256.New, []byte(hashKey)) 157 h.Write(b) 158 sig := h.Sum(nil) 159 if len(sig) != len(parts[2]) || subtle.ConstantTimeCompare(sig, parts[2]) != 1 { 160 return nil, errors.New("Decode: the value is not valid") 161 } 162 // 3. Verify date ranges. 163 var t1 int64 164 if t1, err = strconv.ParseInt(string(parts[0]), 10, 64); err != nil { 165 return nil, errors.New("Decode: invalid timestamp") 166 } 167 t2 := time.Now().UTC().Unix() 168 if t1 > t2 { 169 return nil, errors.New("Decode: timestamp is too new") 170 } 171 if t1 < t2-gcmaxlifetime { 172 return nil, errors.New("Decode: expired timestamp") 173 } 174 // 4. Decrypt (optional). 175 b, err = decode(parts[1]) 176 if err != nil { 177 return nil, err 178 } 179 if b, err = decrypt(block, b); err != nil { 180 return nil, err 181 } 182 // 5. DecodeGob. 183 dst, err := DecodeGob(b) 184 if err != nil { 185 return nil, err 186 } 187 return dst, nil 188 } 189 190 // Encoding ------------------------------------------------------------------- 191 192 // encode encodes a value using base64. 193 func encode(value []byte) []byte { 194 encoded := make([]byte, base64.URLEncoding.EncodedLen(len(value))) 195 base64.URLEncoding.Encode(encoded, value) 196 return encoded 197 } 198 199 // decode decodes a cookie using base64. 200 func decode(value []byte) ([]byte, error) { 201 decoded := make([]byte, base64.URLEncoding.DecodedLen(len(value))) 202 b, err := base64.URLEncoding.Decode(decoded, value) 203 if err != nil { 204 return nil, err 205 } 206 return decoded[:b], nil 207 }