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  }