Add functions for handling Digest headers.
These are not yet integrated into the rest of the library.
このコミットが含まれているのは:
コミット
1307faeb98
|
@ -0,0 +1,88 @@
|
|||
package httpsig
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto"
|
||||
"encoding/base64"
|
||||
"fmt"
|
||||
"hash"
|
||||
"net/http"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type DigestAlgorithm string
|
||||
|
||||
const (
|
||||
DigestSha256 DigestAlgorithm = "SHA-256"
|
||||
DigestSha512 = "SHA-512"
|
||||
)
|
||||
|
||||
var digestToDef = map[DigestAlgorithm]crypto.Hash{
|
||||
DigestSha256: crypto.SHA256,
|
||||
DigestSha512: crypto.SHA512,
|
||||
}
|
||||
|
||||
func getHash(alg DigestAlgorithm) (h hash.Hash, toUse DigestAlgorithm, err error) {
|
||||
upper := DigestAlgorithm(strings.ToUpper(string(alg)))
|
||||
c, ok := digestToDef[upper]
|
||||
if !ok {
|
||||
err = fmt.Errorf("unknown or unsupported Digest algorithm: %s", alg)
|
||||
} else if !c.Available() {
|
||||
err = fmt.Errorf("unavailable Digest algorithm: %s", alg)
|
||||
} else {
|
||||
h = c.New()
|
||||
toUse = upper
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
const (
|
||||
digestHeader = "Digest"
|
||||
digestDelim = "="
|
||||
)
|
||||
|
||||
func addDigest(r *http.Request, algo DigestAlgorithm, b []byte) (err error) {
|
||||
_, ok := r.Header[digestHeader]
|
||||
if ok {
|
||||
err = fmt.Errorf("cannot add Digest: Digest is already set")
|
||||
return
|
||||
}
|
||||
var h hash.Hash
|
||||
var a DigestAlgorithm
|
||||
h, a, err = getHash(algo)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
sum := h.Sum(b)
|
||||
r.Header.Add(digestHeader,
|
||||
fmt.Sprintf("%s%s%s",
|
||||
a,
|
||||
digestDelim,
|
||||
base64.StdEncoding.EncodeToString(sum[:])))
|
||||
return
|
||||
}
|
||||
|
||||
func verifyDigest(r *http.Request, body *bytes.Buffer) (err error) {
|
||||
d := r.Header.Get(digestHeader)
|
||||
if len(d) == 0 {
|
||||
err = fmt.Errorf("cannot verify Digest: request has no Digest header")
|
||||
return
|
||||
}
|
||||
elem := strings.SplitN(d, digestDelim, 2)
|
||||
if len(elem) != 2 {
|
||||
err = fmt.Errorf("cannot verify Digest: malformed Digest: %s", d)
|
||||
return
|
||||
}
|
||||
var h hash.Hash
|
||||
h, _, err = getHash(DigestAlgorithm(elem[0]))
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
sum := h.Sum(body.Bytes())
|
||||
encSum := base64.StdEncoding.EncodeToString(sum[:])
|
||||
if encSum != elem[1] {
|
||||
err = fmt.Errorf("cannot verify Digest: header Digest does not match the digest of the request body")
|
||||
return
|
||||
}
|
||||
return
|
||||
}
|
|
@ -0,0 +1,157 @@
|
|||
package httpsig
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"net/http"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestAddDigest(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
r func() *http.Request
|
||||
algo DigestAlgorithm
|
||||
body []byte
|
||||
expectedDigest string
|
||||
expectError bool
|
||||
}{
|
||||
{
|
||||
name: "adds sha256 digest",
|
||||
r: func() *http.Request {
|
||||
r, _ :=http.NewRequest("POST", "example.com", nil)
|
||||
return r
|
||||
},
|
||||
algo: "SHA-256",
|
||||
body: []byte("johnny grab your gun"),
|
||||
expectedDigest: "SHA-256=am9obm55IGdyYWIgeW91ciBndW7jsMRCmPwcFJr79MiZb7kkJ65B5GSbk0yklZkbeFK4VQ==",
|
||||
},
|
||||
{
|
||||
name: "adds sha512 digest",
|
||||
r: func() *http.Request {
|
||||
r, _ := http.NewRequest("POST", "example.com", nil)
|
||||
return r
|
||||
},
|
||||
algo: "SHA-512",
|
||||
body: []byte("yours is the drill that will pierce the heavens"),
|
||||
expectedDigest: "SHA-512=eW91cnMgaXMgdGhlIGRyaWxsIHRoYXQgd2lsbCBwaWVyY2UgdGhlIGhlYXZlbnPPg+E1fu+4vfFUKFDWbYAH1iDkBQtXFdyD9Kkh02zpzkfQ0TxdhfKw/4MY0od+7C9juTG9R0F6gaU4Mnr5J9o+",
|
||||
},
|
||||
{
|
||||
name: "digest already set",
|
||||
r: func() *http.Request {
|
||||
r, _ := http.NewRequest("POST", "example.com", nil)
|
||||
r.Header.Set("Digest", "oops")
|
||||
return r
|
||||
},
|
||||
algo: "SHA-512",
|
||||
body: []byte("did bob ewell fall on his knife"),
|
||||
expectError: true,
|
||||
},
|
||||
{
|
||||
name: "unknown/unsupported digest algorithm",
|
||||
r: func() *http.Request {
|
||||
r, _ := http.NewRequest("POST", "example.com", nil)
|
||||
return r
|
||||
},
|
||||
algo: "MD5",
|
||||
body: []byte("two times Cuchulainn almost drowned"),
|
||||
expectError: true,
|
||||
},
|
||||
}
|
||||
for _, test := range tests {
|
||||
req := test.r()
|
||||
err := addDigest(req, test.algo, test.body)
|
||||
gotErr := err != nil
|
||||
if gotErr != test.expectError {
|
||||
if test.expectError {
|
||||
t.Fatalf("%q: expected error, got: %s", test.name, err)
|
||||
} else {
|
||||
t.Fatalf("%q: expected no error, got: %s", test.name, err)
|
||||
}
|
||||
} else if !gotErr {
|
||||
d := req.Header.Get("Digest")
|
||||
if d != test.expectedDigest {
|
||||
t.Fatalf("%q: unexpected digest: want %s, got %s", test.name, test.expectedDigest, d)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestVerifyDigest(t *testing.T) {
|
||||
tests := []struct{
|
||||
name string
|
||||
r func() *http.Request
|
||||
body []byte
|
||||
expectError bool
|
||||
}{
|
||||
{
|
||||
name: "verify sha256",
|
||||
r: func() *http.Request {
|
||||
r, _ := http.NewRequest("POST", "example.com", nil)
|
||||
r.Header.Set("Digest", "SHA-256=am9obm55IGdyYWIgeW91ciBndW7jsMRCmPwcFJr79MiZb7kkJ65B5GSbk0yklZkbeFK4VQ==")
|
||||
return r
|
||||
},
|
||||
body: []byte("johnny grab your gun"),
|
||||
},
|
||||
{
|
||||
name: "verify sha512",
|
||||
r: func() *http.Request {
|
||||
r, _ := http.NewRequest("POST", "example.com", nil)
|
||||
r.Header.Set("Digest", "SHA-512=eW91cnMgaXMgdGhlIGRyaWxsIHRoYXQgd2lsbCBwaWVyY2UgdGhlIGhlYXZlbnPPg+E1fu+4vfFUKFDWbYAH1iDkBQtXFdyD9Kkh02zpzkfQ0TxdhfKw/4MY0od+7C9juTG9R0F6gaU4Mnr5J9o+")
|
||||
return r
|
||||
},
|
||||
body: []byte("yours is the drill that will pierce the heavens"),
|
||||
},
|
||||
{
|
||||
name: "no digest header",
|
||||
r: func() *http.Request {
|
||||
r, _ := http.NewRequest("POST", "example.com", nil)
|
||||
return r
|
||||
},
|
||||
body: []byte("Yuji's gender is blue"),
|
||||
expectError: true,
|
||||
},
|
||||
{
|
||||
name: "malformed digest",
|
||||
r: func() *http.Request {
|
||||
r, _ := http.NewRequest("POST", "example.com", nil)
|
||||
r.Header.Set("Digest", "SHA-256am9obm55IGdyYWIgeW91ciBndW7jsMRCmPwcFJr79MiZb7kkJ65B5GSbk0yklZkbeFK4VQ==")
|
||||
return r
|
||||
},
|
||||
body: []byte("Tochee and Ozzie BFFs forever"),
|
||||
expectError: true,
|
||||
},
|
||||
{
|
||||
name: "unsupported/unknown algo",
|
||||
r: func() *http.Request {
|
||||
r, _ := http.NewRequest("POST", "example.com", nil)
|
||||
r.Header.Set("Digest", "MD5=poo")
|
||||
return r
|
||||
},
|
||||
body: []byte("what is a man? a miserable pile of secrets"),
|
||||
expectError: true,
|
||||
},
|
||||
{
|
||||
name: "bad digest",
|
||||
r: func() *http.Request {
|
||||
r, _ := http.NewRequest("POST", "example.com", nil)
|
||||
r.Header.Set("Digest", "SHA-256=bm9obm55IGdyYWIgeW91ciBndW7jsMRCmPwcFJr79MiZb7kkJ65B5GSbk0yklZkbeFK4VQ==")
|
||||
return r
|
||||
},
|
||||
body: []byte("johnny grab your gun"),
|
||||
expectError: true,
|
||||
},
|
||||
}
|
||||
for _, test := range tests {
|
||||
req := test.r()
|
||||
buf := bytes.NewBuffer(test.body)
|
||||
err := verifyDigest(req, buf)
|
||||
gotErr := err != nil
|
||||
if gotErr != test.expectError {
|
||||
if test.expectError {
|
||||
t.Fatalf("%q: expected error, got: %s", test.name, err)
|
||||
} else {
|
||||
t.Fatalf("%q: expected no error, got: %s", test.name, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
読み込み中…
新しいイシューから参照