httpsig/httpsig_test.go

358 行
11 KiB
Go
Raw 通常表示 履歴

package httpsig
import (
"crypto"
"crypto/rand"
"crypto/rsa"
"fmt"
"net/http"
"net/http/httptest"
"strings"
"testing"
)
const (
testUrl = "foo.net/bar/baz?q=test&r=ok"
testDate = "Tue, 07 Jun 2014 20:51:35 GMT"
testDigest = "SHA-256=X48E9qOokqqrvdts8nOJRJN3OWDUoyWxBf7kbu9DBPE="
testMethod = "GET"
)
type httpsigTest struct {
name string
prefs []Algorithm
headers []string
scheme SignatureScheme
privKey crypto.PrivateKey
pubKey crypto.PublicKey
pubKeyId string
expectedAlgorithm Algorithm
expectErrorSigningResponse bool
}
var (
privKey *rsa.PrivateKey
macKey []byte
tests []httpsigTest
)
func init() {
var err error
privKey, err = rsa.GenerateKey(rand.Reader, 2048)
if err != nil {
panic(err)
}
macKey = make([]byte, 128)
err = readFullFromCrypto(macKey)
if err != nil {
panic(err)
}
tests = []httpsigTest{
{
name: "rsa signature",
prefs: []Algorithm{RSA_SHA512},
headers: []string{"Date", "Digest"},
scheme: Signature,
privKey: privKey,
pubKey: privKey.Public(),
pubKeyId: "pubKeyId",
expectedAlgorithm: RSA_SHA512,
},
{
name: "hmac signature",
prefs: []Algorithm{HMAC_SHA256},
headers: []string{"Date", "Digest"},
scheme: Signature,
privKey: macKey,
pubKey: macKey,
pubKeyId: "pubKeyId",
expectedAlgorithm: HMAC_SHA256,
},
{
name: "rsa authorization",
prefs: []Algorithm{RSA_SHA512},
headers: []string{"Date", "Digest"},
scheme: Authorization,
privKey: privKey,
pubKey: privKey.Public(),
pubKeyId: "pubKeyId",
expectedAlgorithm: RSA_SHA512,
},
{
name: "hmac authorization",
prefs: []Algorithm{HMAC_SHA256},
headers: []string{"Date", "Digest"},
scheme: Authorization,
privKey: macKey,
pubKey: macKey,
pubKeyId: "pubKeyId",
expectedAlgorithm: HMAC_SHA256,
},
{
name: "default algo",
headers: []string{"Date", "Digest"},
scheme: Signature,
privKey: privKey,
pubKey: privKey.Public(),
pubKeyId: "pubKeyId",
expectedAlgorithm: RSA_SHA256,
},
{
name: "default headers",
prefs: []Algorithm{RSA_SHA512},
scheme: Signature,
privKey: privKey,
pubKey: privKey.Public(),
pubKeyId: "pubKeyId",
expectedAlgorithm: RSA_SHA512,
},
{
name: "different pub key id",
prefs: []Algorithm{RSA_SHA512},
headers: []string{"Date", "Digest"},
scheme: Signature,
privKey: privKey,
pubKey: privKey.Public(),
pubKeyId: "i write code that sucks",
expectedAlgorithm: RSA_SHA512,
},
{
name: "with request target",
prefs: []Algorithm{RSA_SHA512},
headers: []string{"Date", "Digest", RequestTarget},
scheme: Signature,
privKey: privKey,
pubKey: privKey.Public(),
pubKeyId: "pubKeyId",
expectedAlgorithm: RSA_SHA512,
expectErrorSigningResponse: true,
},
}
}
func toSignatureParameter(k, v string) string {
return fmt.Sprintf("%s%s%s%s%s", k, parameterKVSeparater, parameterValueDelimiter, v, parameterValueDelimiter)
}
func toHeaderSignatureParameters(k string, vals []string) string {
if len(vals) == 0 {
vals = defaultHeaders
}
v := strings.Join(vals, headerParameterValueDelim)
k = strings.ToLower(k)
v = strings.ToLower(v)
return fmt.Sprintf("%s%s%s%s%s", k, parameterKVSeparater, parameterValueDelimiter, v, parameterValueDelimiter)
}
func TestNewSigner(t *testing.T) {
for _, test := range tests {
s, a, err := NewSigner(test.prefs, test.headers, test.scheme)
if err != nil {
t.Fatalf("%q: %s", test.name, err)
}
if a != test.expectedAlgorithm {
t.Fatalf("%q: got %s, want %s", test.name, a, test.expectedAlgorithm)
}
// Test request signing
req, err := http.NewRequest(testMethod, testUrl, nil)
if err != nil {
t.Fatalf("%q: %s", test.name, err)
}
req.Header.Set("Date", testDate)
req.Header.Set("Digest", testDigest)
err = s.SignRequest(test.privKey, test.pubKeyId, req)
if err != nil {
t.Fatalf("%q: %s", test.name, err)
}
vals, ok := req.Header[string(test.scheme)]
if !ok {
t.Fatalf("%q: not in header %s", test.name, test.scheme)
}
if len(vals) != 1 {
t.Fatalf("%q: too many in header %s: %d", test.name, test.scheme, len(vals))
}
if p := toSignatureParameter(keyIdParameter, test.pubKeyId); !strings.Contains(vals[0], p) {
t.Fatalf("%q: %s\ndoes not contain\n%s", test.name, vals[0], p)
} else if p := toSignatureParameter(algorithmParameter, string(test.expectedAlgorithm)); !strings.Contains(vals[0], p) {
t.Fatalf("%q: %s\ndoes not contain\n%s", test.name, vals[0], p)
} else if p := toHeaderSignatureParameters(headersParameter, test.headers); !strings.Contains(vals[0], p) {
t.Fatalf("%q: %s\ndoes not contain\n%s", test.name, vals[0], p)
} else if !strings.Contains(vals[0], signatureParameter) {
t.Fatalf("%q: %s\ndoes not contain\n%s", test.name, vals[0], signatureParameter)
}
// Test response signing
resp := httptest.NewRecorder()
resp.HeaderMap.Set("Date", testDate)
resp.HeaderMap.Set("Digest", testDigest)
err = s.SignResponse(test.privKey, test.pubKeyId, resp)
if test.expectErrorSigningResponse {
if err != nil {
// Skip rest of testing
continue
} else {
t.Fatalf("%q: expected error, got nil", test.name)
}
}
vals, ok = resp.HeaderMap[string(test.scheme)]
if !ok {
t.Fatalf("%q: not in header %s", test.name, test.scheme)
}
if len(vals) != 1 {
t.Fatalf("%q: too many in header %s: %d", test.name, test.scheme, len(vals))
}
if p := toSignatureParameter(keyIdParameter, test.pubKeyId); !strings.Contains(vals[0], p) {
t.Fatalf("%q: %s\ndoes not contain\n%s", test.name, vals[0], p)
} else if p := toSignatureParameter(algorithmParameter, string(test.expectedAlgorithm)); !strings.Contains(vals[0], p) {
t.Fatalf("%q: %s\ndoes not contain\n%s", test.name, vals[0], p)
} else if p := toHeaderSignatureParameters(headersParameter, test.headers); !strings.Contains(vals[0], p) {
t.Fatalf("%q: %s\ndoes not contain\n%s", test.name, vals[0], p)
} else if !strings.Contains(vals[0], signatureParameter) {
t.Fatalf("%q: %s\ndoes not contain\n%s", test.name, vals[0], signatureParameter)
}
}
}
func TestNewSignerRequestMissingHeaders(t *testing.T) {
failingTests := []struct {
name string
prefs []Algorithm
headers []string
scheme SignatureScheme
privKey crypto.PrivateKey
pubKeyId string
expectedAlgorithm Algorithm
}{
{
name: "wants digest",
prefs: []Algorithm{RSA_SHA512},
headers: []string{"Date", "Digest"},
scheme: Signature,
privKey: privKey,
pubKeyId: "pubKeyId",
expectedAlgorithm: RSA_SHA512,
},
}
for _, test := range failingTests {
s, a, err := NewSigner(test.prefs, test.headers, test.scheme)
if err != nil {
t.Fatalf("%q: %s", test.name, err)
}
if a != test.expectedAlgorithm {
t.Fatalf("%q: got %s, want %s", test.name, a, test.expectedAlgorithm)
}
req, err := http.NewRequest(testMethod, testUrl, nil)
if err != nil {
t.Fatalf("%q: %s", test.name, err)
}
req.Header.Set("Date", testDate)
err = s.SignRequest(test.privKey, test.pubKeyId, req)
if err == nil {
t.Fatalf("%q: expect error but got nil", test.name)
}
}
}
func TestNewSignerResponseMissingHeaders(t *testing.T) {
failingTests := []struct {
name string
prefs []Algorithm
headers []string
scheme SignatureScheme
privKey crypto.PrivateKey
pubKeyId string
expectedAlgorithm Algorithm
expectErrorSigningResponse bool
}{
{
name: "want digest",
prefs: []Algorithm{RSA_SHA512},
headers: []string{"Date", "Digest"},
scheme: Signature,
privKey: privKey,
pubKeyId: "pubKeyId",
expectedAlgorithm: RSA_SHA512,
},
}
for _, test := range failingTests {
s, a, err := NewSigner(test.prefs, test.headers, test.scheme)
if err != nil {
t.Fatalf("%q: %s", test.name, err)
}
if a != test.expectedAlgorithm {
t.Fatalf("%q: got %s, want %s", test.name, a, test.expectedAlgorithm)
}
resp := httptest.NewRecorder()
resp.HeaderMap.Set("Date", testDate)
resp.HeaderMap.Set("Digest", testDigest)
err = s.SignResponse(test.privKey, test.pubKeyId, resp)
if err != nil {
t.Fatalf("%q: expected error, got nil", test.name)
}
}
}
func TestNewVerifier(t *testing.T) {
for _, test := range tests {
// Prepare
req, err := http.NewRequest(testMethod, testUrl, nil)
if err != nil {
t.Fatalf("%q: %s", test.name, err)
}
req.Header.Set("Date", testDate)
req.Header.Set("Digest", testDigest)
s, _, err := NewSigner(test.prefs, test.headers, test.scheme)
if err != nil {
t.Fatalf("%q: %s", test.name, err)
}
err = s.SignRequest(test.privKey, test.pubKeyId, req)
if err != nil {
t.Fatalf("%q: %s", test.name, err)
}
// Test verification
v, err := NewVerifier(req)
if err != nil {
t.Fatalf("%q: %s", test.name, err)
}
if v.KeyId() != test.pubKeyId {
t.Fatalf("%q: got %s, want %s", test.name, v.KeyId(), test.pubKeyId)
}
err = v.Verify(test.pubKey, test.expectedAlgorithm)
if err != nil {
t.Fatalf("%q: %s", test.name, err)
}
}
}
func TestNewResponseVerifier(t *testing.T) {
for _, test := range tests {
if test.expectErrorSigningResponse {
continue
}
// Prepare
resp := httptest.NewRecorder()
resp.HeaderMap.Set("Date", testDate)
resp.HeaderMap.Set("Digest", testDigest)
s, _, err := NewSigner(test.prefs, test.headers, test.scheme)
if err != nil {
t.Fatalf("%q: %s", test.name, err)
}
err = s.SignResponse(test.privKey, test.pubKeyId, resp)
if err != nil {
t.Fatalf("%q: %s", test.name, err)
}
// Test verification
v, err := NewResponseVerifier(resp.Result())
if err != nil {
t.Fatalf("%q: %s", test.name, err)
}
if v.KeyId() != test.pubKeyId {
t.Fatalf("%q: got %s, want %s", test.name, v.KeyId(), test.pubKeyId)
}
err = v.Verify(test.pubKey, test.expectedAlgorithm)
if err != nil {
t.Fatalf("%q: %s", test.name, err)
}
}
}