improve hmac validation

feature/refresh
Brad Rydzewski 6 years ago
parent 005219895e
commit dfcb555de1

@ -10,10 +10,9 @@ import (
"io"
"io/ioutil"
"net/http"
"strings"
"github.com/drone/go-scm/scm"
"github.com/drone/go-scm/scm/driver/internal/sha1"
"github.com/drone/go-scm/scm/driver/internal/hmac"
)
type webhookService struct {
@ -59,9 +58,7 @@ func (s *webhookService) Parse(req *http.Request, fn scm.SecretFunc) (scm.Webhoo
}
sig := req.Header.Get("X-Hub-Signature")
sig = strings.TrimPrefix(sig, "sha1=")
if !sha1.ValidateEncoded(data, []byte(key), sig) {
if !hmac.ValidatePrefix(data, []byte(key), sig) {
return hook, scm.ErrSignatureInvalid
}

@ -5,6 +5,7 @@
package gogs
import (
"crypto/sha256"
"encoding/json"
"fmt"
"io"
@ -63,7 +64,7 @@ func (s *webhookService) Parse(req *http.Request, fn scm.SecretFunc) (scm.Webhoo
return hook, scm.ErrSignatureInvalid
}
if !hmac.ValidateEncoded(data, []byte(key), sig) {
if !hmac.Validate(sha256.New, data, []byte(key), sig) {
return hook, scm.ErrSignatureInvalid
}

@ -6,24 +6,43 @@ package hmac
import (
"crypto/hmac"
"crypto/sha1"
"crypto/sha256"
"encoding/hex"
"hash"
"strings"
)
// Validate checks the hmac signature of the message.
func Validate(message, key, signature []byte) bool {
mac := hmac.New(sha256.New, key)
mac.Write(message)
sum := mac.Sum(nil)
return hmac.Equal(signature, sum)
}
// ValidateEncoded checks the hmac signature of the mssasge
// Validate checks the hmac signature of the mssasge
// using a hex encoded signature.
func ValidateEncoded(message, key []byte, signature string) bool {
func Validate(h func() hash.Hash, message, key []byte, signature string) bool {
decoded, err := hex.DecodeString(signature)
if err != nil {
return false
}
return Validate(message, key, decoded)
return validate(h, message, key, decoded)
}
// ValidatePrefix checks the hmac signature of the message
// using the message prefix to determine the signing algorithm.
func ValidatePrefix(message, key []byte, signature string) bool {
parts := strings.Split(signature, "=")
if len(parts) != 2 {
return false
}
switch parts[0] {
case "sha1":
return Validate(sha1.New, message, key, parts[1])
case "sha256":
return Validate(sha256.New, message, key, parts[1])
default:
return false
}
}
func validate(h func() hash.Hash, message, key, signature []byte) bool {
mac := hmac.New(h, key)
mac.Write(message)
sum := mac.Sum(nil)
return hmac.Equal(signature, sum)
}

@ -5,38 +5,72 @@
package hmac
import (
"crypto/md5"
"crypto/sha1"
"crypto/sha256"
"hash"
"testing"
)
func TestValidate(t *testing.T) {
func TestValidatePrefix(t *testing.T) {
tests := []struct {
msg string
key string
sig string
res bool
}{
//
// sha256
//
{
msg: "bonjour monde",
key: "topsecret",
sig: "sha256=8ca57e2afbad9fea8860404575c2d61827995c62aacd4c514eae4c404896390b",
res: true,
},
{
msg: "hello world",
key: "",
sig: "512d57985a1de49af20cfa7b785c07a701a0b7b5f2ff06726b3ac077066c1992",
key: "topsecret",
sig: "sha256=8ca57e2afbad9fea8860404575c2d61827995c62aacd4c514eae4c404896390b",
res: false,
},
//
// sha1
//
{
msg: "hola mundo",
key: "",
sig: "",
msg: "bonjour monde",
key: "topsecret",
sig: "sha1=f25bad540601ff3131736e24a48dd928fa9ccc93",
res: false,
},
{
msg: "bonjour monde",
key: "root",
sig: "512d57985a1de49af20cfa7b785c07a701a0b7b5f2ff06726b3ac077066c1992",
msg: "hello world",
key: "topsecret",
sig: "sha1=f25bad540601ff3131736e24a48dd928fa9ccc93",
res: true,
},
//
// algorithm not supported
//
{
msg: "bonjour monde",
key: "topsecret",
sig: "md5=f7e48000ca338292875859154295d2fe",
res: false,
},
//
// algorithm not specified
//
{
msg: "bonjour monde",
key: "topsecret",
sig: "8ca57e2afbad9fea8860404575c2d61827995c62aacd4c514eae4c404896390b",
res: false,
},
}
for _, test := range tests {
res := ValidateEncoded(
res := ValidatePrefix(
[]byte(test.msg),
[]byte(test.key),
test.sig,
@ -46,5 +80,89 @@ func TestValidate(t *testing.T) {
test.res, test.msg)
}
}
}
func TestValidate(t *testing.T) {
tests := []struct {
alg func() hash.Hash
msg string
key string
sig string
res bool
}{
//
// sha256
//
{
alg: sha256.New,
msg: "bonjour monde",
key: "topsecret",
sig: "8ca57e2afbad9fea8860404575c2d61827995c62aacd4c514eae4c404896390b",
res: true,
},
{
alg: sha256.New,
msg: "hello world",
key: "topsecret",
sig: "8ca57e2afbad9fea8860404575c2d61827995c62aacd4c514eae4c404896390b",
res: false,
},
//
// sha1
//
{
alg: sha1.New,
msg: "hello world",
key: "topsecret",
sig: "f25bad540601ff3131736e24a48dd928fa9ccc93",
res: true,
},
{
alg: sha1.New,
msg: "bonjour monde",
key: "topsecret",
sig: "f25bad540601ff3131736e24a48dd928fa9ccc93",
res: false,
},
//
// md5
//
{
alg: md5.New,
msg: "hello world",
key: "topsecret",
sig: "223a982e3a9eeaf1ebae1b458464d90b",
res: true,
},
{
alg: md5.New,
msg: "bonjour monde",
key: "topsecret",
sig: "223a982e3a9eeaf1ebae1b458464d90b",
res: false,
},
//
// invalid hex
//
{
alg: sha1.New,
msg: "hello world",
key: "topsecret",
sig: "f25bad540601ff3131736e24a48dd928fa9ccc93==",
res: false,
},
}
for _, test := range tests {
res := Validate(
test.alg,
[]byte(test.msg),
[]byte(test.key),
test.sig,
)
if res != test.res {
t.Errorf("Want valid %v for message %q",
test.res, test.msg)
}
}
}

@ -1,29 +0,0 @@
// Copyright 2017 Drone.IO Inc. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package sha1
import (
"crypto/hmac"
"crypto/sha1"
"encoding/hex"
)
// Validate checks the hmac signature of the message.
func Validate(message, key, signature []byte) bool {
mac := hmac.New(sha1.New, key)
mac.Write(message)
sum := mac.Sum(nil)
return hmac.Equal(signature, sum)
}
// ValidateEncoded checks the hmac signature of the mssasge
// using a hex encoded signature.
func ValidateEncoded(message, key []byte, signature string) bool {
decoded, err := hex.DecodeString(signature)
if err != nil {
return false
}
return Validate(message, key, decoded)
}

@ -1,37 +0,0 @@
// Copyright 2017 Drone.IO Inc. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package sha1
import (
"testing"
)
func TestValidate(t *testing.T) {
tests := []struct {
msg string
key string
sig string
res bool
}{
{
msg: `{"yo":true}`,
key: "0123456789abcdef",
sig: "126f2c800419c60137ce748d7672e77b65cf16d6",
res: true,
},
}
for _, test := range tests {
res := ValidateEncoded(
[]byte(test.msg),
[]byte(test.key),
test.sig,
)
if res != test.res {
t.Errorf("Want valid %v for message %q",
test.res, test.msg)
}
}
}

@ -10,7 +10,6 @@ import (
"io"
"io/ioutil"
"net/http"
"strings"
"time"
"github.com/drone/go-scm/scm"
@ -64,9 +63,7 @@ func (s *webhookService) Parse(req *http.Request, fn scm.SecretFunc) (scm.Webhoo
}
sig := req.Header.Get("X-Hub-Signature")
sig = strings.TrimPrefix(sig, "sha256=")
if !hmac.ValidateEncoded(data, []byte(key), sig) {
if !hmac.ValidatePrefix(data, []byte(key), sig) {
return hook, scm.ErrSignatureInvalid
}

Loading…
Cancel
Save