@@ 10,6 10,7 @@ import (
"errors"
"fmt"
"io"
+ mrand "math/rand"
"net/http"
"strings"
@@ 31,18 32,34 @@ var (
// key without affecting already encrypted/signed data that's
// in the wild.
//
-// The first key in `Keys` should be the current main key
+// # The first key in `Keys` should be the current main key
+//
+// If `Reset` is true then if any key, other than the current
+// main key was used for the cookie signing/encryption it will
+// resign/encrypt the value with the current main key. Mainly
+// useful for edge cases where you need to phase out an old
+// key for whatever reason
type KeyWallet struct {
- Keys [][]byte
+ Keys [][]byte
+ Reset bool
}
// GenerateSecretKey will generate a random 32byte key used for cookie
// signing and/or encryption
-func GenerateSecretKey() []byte {
- key := make([]byte, 32)
+func GenerateSecretKey(keylen int, alpha bool) []byte {
+ key := make([]byte, keylen)
+ if alpha {
+ chars := `abcdefghijkmnpqrstuvwxyzABCDEFGHIJKLMNPQRSTUVWXYZ0123456789!@#$%^&*()-_=+[]{}\\|'";:/?.>,<`
+ for i := 0; i < keylen; i++ {
+ key[i] = chars[mrand.Intn(len(chars))]
+ }
+ return key
+ }
+
_, err := rand.Read(key)
if err != nil {
- // handle error here
+ // XXX Panic?
+ panic(err)
}
return key
}
@@ 99,8 116,8 @@ func GetEncode(c echo.Context, name string) (*http.Cookie, error) {
}
// SetSigned will set a cookie and sign the value
-func SetSigned(c echo.Context, cookie *http.Cookie, key *KeyWallet) error {
- mac := hmac.New(sha256.New, key.Keys[0])
+func SetSigned(c echo.Context, cookie *http.Cookie, kw *KeyWallet) error {
+ mac := hmac.New(sha256.New, kw.Keys[0])
mac.Write([]byte(cookie.Name))
mac.Write([]byte(cookie.Value))
signature := mac.Sum(nil)
@@ 116,7 133,7 @@ func SetSigned(c echo.Context, cookie *http.Cookie, key *KeyWallet) error {
}
// GetSigned will get a cookie and verify it's signature
-func GetSigned(c echo.Context, name string, key *KeyWallet) (*http.Cookie, error) {
+func GetSigned(c echo.Context, name string, kw *KeyWallet) (*http.Cookie, error) {
cookie, err := Get(c, name)
if err != nil {
return nil, err
@@ 136,9 153,9 @@ func GetSigned(c echo.Context, name string, key *KeyWallet) (*http.Cookie, error
// Loop through all keys
var found bool
- for _, _key := range key.Keys {
+ for i, key := range kw.Keys {
//Recalculate the HMAC signature of the cookie name and original value.
- mac := hmac.New(sha256.New, _key)
+ mac := hmac.New(sha256.New, key)
mac.Write([]byte(name))
mac.Write([]byte(value))
expectedSignature := mac.Sum(nil)
@@ 149,7 166,12 @@ func GetSigned(c echo.Context, name string, key *KeyWallet) (*http.Cookie, error
if !hmac.Equal([]byte(signature), expectedSignature) {
continue
}
+
cookie.Value = value
+ if i != 0 && kw.Reset {
+ SetSigned(c, cookie, kw)
+ cookie.Value = value
+ }
found = true
break
}
@@ 162,9 184,9 @@ func GetSigned(c echo.Context, name string, key *KeyWallet) (*http.Cookie, error
}
// SetEncrypted will set a cookie that is encrypted
-func SetEncrypted(c echo.Context, cookie *http.Cookie, key *KeyWallet) error {
+func SetEncrypted(c echo.Context, cookie *http.Cookie, kw *KeyWallet) error {
// Create a new AES cipher block from the secret key.
- block, err := aes.NewCipher(key.Keys[0])
+ block, err := aes.NewCipher(kw.Keys[0])
if err != nil {
return err
}
@@ 205,7 227,7 @@ func SetEncrypted(c echo.Context, cookie *http.Cookie, key *KeyWallet) error {
}
// GetEncrypted will get an encrypted cookie and decrypt it
-func GetEncrypted(c echo.Context, name string, key *KeyWallet) (*http.Cookie, error) {
+func GetEncrypted(c echo.Context, name string, kw *KeyWallet) (*http.Cookie, error) {
cookie, err := Get(c, name)
if err != nil {
return nil, err
@@ 216,9 238,9 @@ func GetEncrypted(c echo.Context, name string, key *KeyWallet) (*http.Cookie, er
}
var found bool
- for _, _key := range key.Keys {
+ for i, key := range kw.Keys {
// Create a new AES cipher block from the secret key.
- block, err := aes.NewCipher(_key)
+ block, err := aes.NewCipher(key)
if err != nil {
// XXX Log error here?
continue
@@ 268,6 290,10 @@ func GetEncrypted(c echo.Context, name string, key *KeyWallet) (*http.Cookie, er
}
cookie.Value = value
+ if i != 0 && kw.Reset {
+ SetEncrypted(c, cookie, kw)
+ cookie.Value = value
+ }
found = true
break
}