Skip to content

Instantly share code, notes, and snippets.

@ytkang
Last active April 15, 2019 07:05
Show Gist options
  • Save ytkang/4b87a98efdc47329591e75d8b3c1b945 to your computer and use it in GitHub Desktop.
Save ytkang/4b87a98efdc47329591e75d8b3c1b945 to your computer and use it in GitHub Desktop.
flask
// this is golang implementation of python flask secure cookie's create_signed_value, decode_signed_value
// create_signed_value: CreateSecret
// decode_signed_value: DecodeSecret
const (
SECRET = "secret"
SECRET_NAME = "secret"
SECRET_EXPIRE = 60 * 60
)
func TimeIndependentEquals(a, b []byte) bool {
if len(a) != len(b) {
fmt.Println("diff length")
return false
}
result := byte(0)
for i := range a {
x := a[i]
y := b[i]
result |= x ^ y
}
return result == 0
}
func CreateSignatureV2(message []byte, secret string) []byte {
key := []byte(secret)
h := hmac.New(sha256.New, key)
h.Write(message)
src := h.Sum(nil)
dst := make([]byte, hex.EncodedLen(len(src)))
hex.Encode(dst, src)
return dst
}
func FormField(value string) string {
return fmt.Sprintf("%d:%s", len(value), value)
}
// use these two function
func DecodeSecret(input []byte) (success bool, valueField []byte) {
success = false
valueField = nil
defer func() {
if r := recover(); r != nil {
log.Println("DecodeSecret error", r)
}
}()
value := input[2:]
arr := bytes.Split(value, []byte(":"))
size := len(arr)
result := make([][]byte, size + 1)
nextLength := 0
for i, element := range arr {
v := bytes.Split(element,[]byte("|"))
if len(v[0]) >= 0 && nextLength == 0 {
result[i] = v[0]
} else {
result[i] = v[0][:nextLength]
}
if len(v) > 1 && i < (size-1) {
nextLength, _ = strconv.Atoi(string(v[1][:]))
} else if len(v) > 1 && i == size -1 {
result[i+1] = v[1]
}
}
nameField := result[size-2]
if string(nameField) != SECRET_NAME {
return
}
timestamp, parseErr := strconv.ParseInt(string(result[size-3][:]), 10, 64)
if parseErr != nil {
return
}
if timestamp < int64(util.NowSeconds() - SECRET_EXPIRE) {
return
}
valueField = result[size-1]
passedSig := result[size]
idx := len(input) - len(passedSig)
signedString := input[:idx]
expectedSig := CreateSignatureV2(signedString, SECRET)
success = TimeIndependentEquals(passedSig, expectedSig)
return
}
func CreateSecret(value string) string {
value = base64.StdEncoding.EncodeToString([]byte(value))
now := int(time.Now().Unix())
toSign := strings.Join([]string{"2",
FormField("0"),
FormField(strconv.Itoa(now)),
FormField(SECRET_NAME),
FormField(value),
"",
}, "|")
signature := string(CreateSignatureV2([]byte(toSign), SECRET))
return toSign+signature
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment