package bitbucketserver import ( "crypto/hmac" "crypto/sha1" // nolint: gosec "crypto/sha256" "crypto/sha512" "encoding/hex" "fmt" "hash" "strings" "github.com/pkg/errors" ) // Attribution: This code is taken from https://github.com/google/go-github. func ValidateSignature(payload []byte, signature string, secretKey []byte) error { messageMAC, hashFunc, err := messageMAC(signature) if err != nil { return err } if !checkMAC(payload, messageMAC, secretKey, hashFunc) { return errors.New("payload signature check failed") } return nil } // genMAC generates the HMAC signature for a message provided the secret key // and hashFunc. func genMAC(message, key []byte, hashFunc func() hash.Hash) []byte { mac := hmac.New(hashFunc, key) // nolint: errcheck mac.Write(message) return mac.Sum(nil) } // checkMAC reports whether messageMAC is a valid HMAC tag for message. func checkMAC(message, messageMAC, key []byte, hashFunc func() hash.Hash) bool { expectedMAC := genMAC(message, key, hashFunc) return hmac.Equal(messageMAC, expectedMAC) } // messageMAC returns the hex-decoded HMAC tag from the signature and its // corresponding hash function. func messageMAC(signature string) ([]byte, func() hash.Hash, error) { if signature == "" { return nil, nil, errors.New("missing signature") } sigParts := strings.SplitN(signature, "=", 2) if len(sigParts) != 2 { return nil, nil, fmt.Errorf("error parsing signature %q", signature) } var hashFunc func() hash.Hash switch sigParts[0] { case "sha1": hashFunc = sha1.New case "sha256": hashFunc = sha256.New case "sha512": hashFunc = sha512.New default: return nil, nil, fmt.Errorf("unknown hash type prefix: %q", sigParts[0]) } buf, err := hex.DecodeString(sigParts[1]) if err != nil { return nil, nil, fmt.Errorf("error decoding signature %q: %v", signature, err) } return buf, hashFunc, nil }