2018-05-18 07:04:09 +09:00
package httpsig
import (
2019-03-10 17:56:13 +09:00
"bytes"
2018-05-18 07:04:09 +09:00
"crypto"
"crypto/rand"
"crypto/rsa"
2019-03-10 17:56:13 +09:00
"crypto/sha256"
"crypto/x509"
"encoding/base64"
"encoding/pem"
2018-05-18 07:04:09 +09:00
"fmt"
2019-03-10 17:56:13 +09:00
"io/ioutil"
2018-05-18 07:04:09 +09:00
"net/http"
"net/http/httptest"
2019-03-10 17:56:13 +09:00
"strconv"
2018-05-18 07:04:09 +09:00
"strings"
"testing"
)
const (
2019-03-10 07:56:49 +09:00
testUrl = "foo.net/bar/baz?q=test&r=ok"
testUrlPath = "bar/baz"
testDate = "Tue, 07 Jun 2014 20:51:35 GMT"
testDigest = "SHA-256=X48E9qOokqqrvdts8nOJRJN3OWDUoyWxBf7kbu9DBPE="
testMethod = "GET"
2018-05-18 07:04:09 +09:00
)
type httpsigTest struct {
name string
prefs [ ] Algorithm
2019-09-07 04:16:36 +09:00
digestAlg DigestAlgorithm
2018-05-18 07:04:09 +09:00
headers [ ] string
2019-09-07 04:16:36 +09:00
body [ ] byte
2018-05-18 07:04:09 +09:00
scheme SignatureScheme
privKey crypto . PrivateKey
pubKey crypto . PublicKey
pubKeyId string
expectedAlgorithm Algorithm
expectErrorSigningResponse bool
2019-03-10 07:56:49 +09:00
expectRequestPath bool
2019-09-07 04:16:36 +09:00
expectedDigest string
2018-05-18 07:04:09 +09:00
}
var (
2019-03-10 17:56:13 +09:00
privKey * rsa . PrivateKey
macKey [ ] byte
tests [ ] httpsigTest
testSpecRSAPrivateKey * rsa . PrivateKey
testSpecRSAPublicKey * rsa . PublicKey
2018-05-18 07:04:09 +09:00
)
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 } ,
2019-09-07 04:16:36 +09:00
digestAlg : DigestSha256 ,
2018-05-18 07:04:09 +09:00
headers : [ ] string { "Date" , "Digest" } ,
scheme : Signature ,
privKey : privKey ,
pubKey : privKey . Public ( ) ,
pubKeyId : "pubKeyId" ,
expectedAlgorithm : RSA_SHA512 ,
} ,
2019-09-07 04:16:36 +09:00
{
name : "digest on rsa signature" ,
prefs : [ ] Algorithm { RSA_SHA512 } ,
digestAlg : DigestSha256 ,
headers : [ ] string { "Date" , "Digest" } ,
body : [ ] byte ( "Last night as I lay dreaming This strangest kind of feeling Revealed its secret meaning And now I know..." ) ,
scheme : Signature ,
privKey : privKey ,
pubKey : privKey . Public ( ) ,
pubKeyId : "pubKeyId" ,
expectedAlgorithm : RSA_SHA512 ,
expectedDigest : "SHA-256=TGFzdCBuaWdodCBhcyBJIGxheSBkcmVhbWluZyBUaGlzIHN0cmFuZ2VzdCBraW5kIG9mIGZlZWxpbmcgUmV2ZWFsZWQgaXRzIHNlY3JldCBtZWFuaW5nIEFuZCBub3cgSSBrbm93Li4u47DEQpj8HBSa+/TImW+5JCeuQeRkm5NMpJWZG3hSuFU=" ,
} ,
2018-05-18 07:04:09 +09:00
{
name : "hmac signature" ,
prefs : [ ] Algorithm { HMAC_SHA256 } ,
2019-09-07 04:16:36 +09:00
digestAlg : DigestSha256 ,
headers : [ ] string { "Date" , "Digest" } ,
scheme : Signature ,
privKey : macKey ,
pubKey : macKey ,
pubKeyId : "pubKeyId" ,
expectedAlgorithm : HMAC_SHA256 ,
} ,
{
name : "digest on hmac signature" ,
prefs : [ ] Algorithm { HMAC_SHA256 } ,
digestAlg : DigestSha256 ,
2018-05-18 07:04:09 +09:00
headers : [ ] string { "Date" , "Digest" } ,
2019-09-07 04:16:36 +09:00
body : [ ] byte ( "I've never ever been to paradise I've never ever seen no angel's eyes You'll never ever let this magic die No matter where you are, you are my lucky star." ) ,
2018-05-18 07:04:09 +09:00
scheme : Signature ,
privKey : macKey ,
pubKey : macKey ,
pubKeyId : "pubKeyId" ,
expectedAlgorithm : HMAC_SHA256 ,
2019-09-07 04:16:36 +09:00
expectedDigest : "SHA-256=SSd2ZSBuZXZlciBldmVyIGJlZW4gdG8gcGFyYWRpc2UgSSd2ZSBuZXZlciBldmVyIHNlZW4gbm8gYW5nZWwncyBleWVzIFlvdSdsbCBuZXZlciBldmVyIGxldCB0aGlzIG1hZ2ljIGRpZSBObyBtYXR0ZXIgd2hlcmUgeW91IGFyZSwgeW91IGFyZSBteSBsdWNreSBzdGFyLuOwxEKY/BwUmvv0yJlvuSQnrkHkZJuTTKSVmRt4UrhV" ,
2018-05-18 07:04:09 +09:00
} ,
{
name : "rsa authorization" ,
prefs : [ ] Algorithm { RSA_SHA512 } ,
2019-09-07 04:16:36 +09:00
digestAlg : DigestSha256 ,
2018-05-18 07:04:09 +09:00
headers : [ ] string { "Date" , "Digest" } ,
scheme : Authorization ,
privKey : privKey ,
pubKey : privKey . Public ( ) ,
pubKeyId : "pubKeyId" ,
expectedAlgorithm : RSA_SHA512 ,
} ,
{
name : "hmac authorization" ,
prefs : [ ] Algorithm { HMAC_SHA256 } ,
2019-09-07 04:16:36 +09:00
digestAlg : DigestSha256 ,
2018-05-18 07:04:09 +09:00
headers : [ ] string { "Date" , "Digest" } ,
scheme : Authorization ,
privKey : macKey ,
pubKey : macKey ,
pubKeyId : "pubKeyId" ,
expectedAlgorithm : HMAC_SHA256 ,
} ,
{
name : "default algo" ,
2019-09-07 04:16:36 +09:00
digestAlg : DigestSha256 ,
2018-05-18 07:04:09 +09:00
headers : [ ] string { "Date" , "Digest" } ,
scheme : Signature ,
privKey : privKey ,
pubKey : privKey . Public ( ) ,
pubKeyId : "pubKeyId" ,
expectedAlgorithm : RSA_SHA256 ,
} ,
{
name : "default headers" ,
prefs : [ ] Algorithm { RSA_SHA512 } ,
2019-09-07 04:16:36 +09:00
digestAlg : DigestSha256 ,
2018-05-18 07:04:09 +09:00
scheme : Signature ,
privKey : privKey ,
pubKey : privKey . Public ( ) ,
pubKeyId : "pubKeyId" ,
expectedAlgorithm : RSA_SHA512 ,
} ,
{
name : "different pub key id" ,
prefs : [ ] Algorithm { RSA_SHA512 } ,
2019-09-07 04:16:36 +09:00
digestAlg : DigestSha256 ,
2018-05-18 07:04:09 +09:00
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 } ,
2019-09-07 04:16:36 +09:00
digestAlg : DigestSha256 ,
2018-05-18 07:04:09 +09:00
headers : [ ] string { "Date" , "Digest" , RequestTarget } ,
scheme : Signature ,
privKey : privKey ,
pubKey : privKey . Public ( ) ,
pubKeyId : "pubKeyId" ,
expectedAlgorithm : RSA_SHA512 ,
expectErrorSigningResponse : true ,
2019-03-10 07:56:49 +09:00
expectRequestPath : true ,
2018-05-18 07:04:09 +09:00
} ,
}
2019-03-10 17:56:13 +09:00
testSpecRSAPrivateKey , err = loadPrivateKey ( [ ] byte ( testSpecPrivateKeyPEM ) )
if err != nil {
panic ( err )
}
testSpecRSAPublicKey , err = loadPublicKey ( [ ] byte ( testSpecPublicKeyPEM ) )
if err != nil {
panic ( err )
}
2018-05-18 07:04:09 +09:00
}
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 )
}
2019-03-10 07:56:49 +09:00
func TestSignerRequest ( t * testing . T ) {
testFn := func ( t * testing . T , test httpsigTest ) {
2019-09-07 04:16:36 +09:00
s , a , err := NewSigner ( test . prefs , test . digestAlg , test . headers , test . scheme )
2018-05-18 07:04:09 +09:00
if err != nil {
2019-03-10 07:56:49 +09:00
t . Fatalf ( "%s" , err )
2018-05-18 07:04:09 +09:00
}
if a != test . expectedAlgorithm {
2019-03-10 07:56:49 +09:00
t . Fatalf ( "got %s, want %s" , a , test . expectedAlgorithm )
2018-05-18 07:04:09 +09:00
}
// Test request signing
req , err := http . NewRequest ( testMethod , testUrl , nil )
if err != nil {
2019-03-10 07:56:49 +09:00
t . Fatalf ( "%s" , err )
2018-05-18 07:04:09 +09:00
}
req . Header . Set ( "Date" , testDate )
2019-09-07 04:16:36 +09:00
if test . body == nil {
req . Header . Set ( "Digest" , testDigest )
}
err = s . SignRequest ( test . privKey , test . pubKeyId , req , test . body )
2018-05-18 07:04:09 +09:00
if err != nil {
2019-03-10 07:56:49 +09:00
t . Fatalf ( "%s" , err )
2018-05-18 07:04:09 +09:00
}
vals , ok := req . Header [ string ( test . scheme ) ]
if ! ok {
2019-03-10 07:56:49 +09:00
t . Fatalf ( "not in header %s" , test . scheme )
2018-05-18 07:04:09 +09:00
}
if len ( vals ) != 1 {
2019-03-10 07:56:49 +09:00
t . Fatalf ( "too many in header %s: %d" , test . scheme , len ( vals ) )
2018-05-18 07:04:09 +09:00
}
if p := toSignatureParameter ( keyIdParameter , test . pubKeyId ) ; ! strings . Contains ( vals [ 0 ] , p ) {
2019-03-10 07:56:49 +09:00
t . Fatalf ( "%s\ndoes not contain\n%s" , vals [ 0 ] , p )
2018-05-18 07:04:09 +09:00
} else if p := toSignatureParameter ( algorithmParameter , string ( test . expectedAlgorithm ) ) ; ! strings . Contains ( vals [ 0 ] , p ) {
2019-03-10 07:56:49 +09:00
t . Fatalf ( "%s\ndoes not contain\n%s" , vals [ 0 ] , p )
2018-05-18 07:04:09 +09:00
} else if p := toHeaderSignatureParameters ( headersParameter , test . headers ) ; ! strings . Contains ( vals [ 0 ] , p ) {
2019-03-10 07:56:49 +09:00
t . Fatalf ( "%s\ndoes not contain\n%s" , vals [ 0 ] , p )
2018-05-18 07:04:09 +09:00
} else if ! strings . Contains ( vals [ 0 ] , signatureParameter ) {
2019-03-10 07:56:49 +09:00
t . Fatalf ( "%s\ndoes not contain\n%s" , vals [ 0 ] , signatureParameter )
2019-09-07 04:16:36 +09:00
} else if test . body != nil && req . Header . Get ( "Digest" ) != test . expectedDigest {
t . Fatalf ( "%s\ndoes not match\n%s" , req . Header . Get ( "Digest" ) , test . expectedDigest )
2019-03-10 07:56:49 +09:00
}
// For schemes with an authScheme, enforce its is present and at the beginning
if len ( test . scheme . authScheme ( ) ) > 0 {
if ! strings . HasPrefix ( vals [ 0 ] , test . scheme . authScheme ( ) ) {
t . Fatalf ( "%s\ndoes not start with\n%s" , vals [ 0 ] , test . scheme . authScheme ( ) )
}
2018-05-18 07:04:09 +09:00
}
2019-03-10 07:56:49 +09:00
}
for _ , test := range tests {
t . Run ( test . name , func ( t * testing . T ) {
testFn ( t , test )
} )
}
}
func TestSignerResponse ( t * testing . T ) {
testFn := func ( t * testing . T , test httpsigTest ) {
2019-09-07 04:16:36 +09:00
s , _ , err := NewSigner ( test . prefs , test . digestAlg , test . headers , test . scheme )
2018-05-18 07:04:09 +09:00
// Test response signing
resp := httptest . NewRecorder ( )
resp . HeaderMap . Set ( "Date" , testDate )
2019-09-07 04:16:36 +09:00
if test . body == nil {
resp . HeaderMap . Set ( "Digest" , testDigest )
}
err = s . SignResponse ( test . privKey , test . pubKeyId , resp , test . body )
2018-05-18 07:04:09 +09:00
if test . expectErrorSigningResponse {
if err != nil {
// Skip rest of testing
2019-03-10 07:56:49 +09:00
return
2018-05-18 07:04:09 +09:00
} else {
2019-03-10 07:56:49 +09:00
t . Fatalf ( "expected error, got nil" )
2018-05-18 07:04:09 +09:00
}
}
2019-03-10 07:56:49 +09:00
vals , ok := resp . HeaderMap [ string ( test . scheme ) ]
2018-05-18 07:04:09 +09:00
if ! ok {
2019-03-10 07:56:49 +09:00
t . Fatalf ( "not in header %s" , test . scheme )
2018-05-18 07:04:09 +09:00
}
if len ( vals ) != 1 {
2019-03-10 07:56:49 +09:00
t . Fatalf ( "too many in header %s: %d" , test . scheme , len ( vals ) )
2018-05-18 07:04:09 +09:00
}
if p := toSignatureParameter ( keyIdParameter , test . pubKeyId ) ; ! strings . Contains ( vals [ 0 ] , p ) {
2019-03-10 07:56:49 +09:00
t . Fatalf ( "%s\ndoes not contain\n%s" , vals [ 0 ] , p )
2018-05-18 07:04:09 +09:00
} else if p := toSignatureParameter ( algorithmParameter , string ( test . expectedAlgorithm ) ) ; ! strings . Contains ( vals [ 0 ] , p ) {
2019-03-10 07:56:49 +09:00
t . Fatalf ( "%s\ndoes not contain\n%s" , vals [ 0 ] , p )
2018-05-18 07:04:09 +09:00
} else if p := toHeaderSignatureParameters ( headersParameter , test . headers ) ; ! strings . Contains ( vals [ 0 ] , p ) {
2019-03-10 07:56:49 +09:00
t . Fatalf ( "%s\ndoes not contain\n%s" , vals [ 0 ] , p )
2018-05-18 07:04:09 +09:00
} else if ! strings . Contains ( vals [ 0 ] , signatureParameter ) {
2019-03-10 07:56:49 +09:00
t . Fatalf ( "%s\ndoes not contain\n%s" , vals [ 0 ] , signatureParameter )
2019-09-07 04:16:36 +09:00
} else if test . body != nil && resp . Header ( ) . Get ( "Digest" ) != test . expectedDigest {
t . Fatalf ( "%s\ndoes not match\n%s" , resp . Header ( ) . Get ( "Digest" ) , test . expectedDigest )
2019-03-10 07:56:49 +09:00
}
// For schemes with an authScheme, enforce its is present and at the beginning
if len ( test . scheme . authScheme ( ) ) > 0 {
if ! strings . HasPrefix ( vals [ 0 ] , test . scheme . authScheme ( ) ) {
t . Fatalf ( "%s\ndoes not start with\n%s" , vals [ 0 ] , test . scheme . authScheme ( ) )
}
2018-05-18 07:04:09 +09:00
}
}
2019-03-10 07:56:49 +09:00
for _ , test := range tests {
t . Run ( test . name , func ( t * testing . T ) {
testFn ( t , test )
} )
}
2018-05-18 07:04:09 +09:00
}
func TestNewSignerRequestMissingHeaders ( t * testing . T ) {
failingTests := [ ] struct {
name string
prefs [ ] Algorithm
2019-09-07 04:16:36 +09:00
digestAlg DigestAlgorithm
2018-05-18 07:04:09 +09:00
headers [ ] string
scheme SignatureScheme
privKey crypto . PrivateKey
pubKeyId string
expectedAlgorithm Algorithm
} {
{
name : "wants digest" ,
prefs : [ ] Algorithm { RSA_SHA512 } ,
2019-09-07 04:16:36 +09:00
digestAlg : DigestSha256 ,
2018-05-18 07:04:09 +09:00
headers : [ ] string { "Date" , "Digest" } ,
scheme : Signature ,
privKey : privKey ,
pubKeyId : "pubKeyId" ,
expectedAlgorithm : RSA_SHA512 ,
} ,
}
for _ , test := range failingTests {
2019-03-10 07:56:49 +09:00
t . Run ( test . name , func ( t * testing . T ) {
test := test
2019-09-07 04:16:36 +09:00
s , a , err := NewSigner ( test . prefs , test . digestAlg , test . headers , test . scheme )
2019-03-10 07:56:49 +09:00
if err != nil {
t . Fatalf ( "%s" , err )
}
if a != test . expectedAlgorithm {
t . Fatalf ( "got %s, want %s" , a , test . expectedAlgorithm )
}
req , err := http . NewRequest ( testMethod , testUrl , nil )
if err != nil {
t . Fatalf ( "%s" , err )
}
req . Header . Set ( "Date" , testDate )
2019-09-07 04:16:36 +09:00
err = s . SignRequest ( test . privKey , test . pubKeyId , req , nil )
2019-03-10 07:56:49 +09:00
if err == nil {
t . Fatalf ( "expect error but got nil" )
}
} )
2018-05-18 07:04:09 +09:00
}
}
func TestNewSignerResponseMissingHeaders ( t * testing . T ) {
failingTests := [ ] struct {
name string
prefs [ ] Algorithm
2019-09-07 04:16:36 +09:00
digestAlg DigestAlgorithm
2018-05-18 07:04:09 +09:00
headers [ ] string
scheme SignatureScheme
privKey crypto . PrivateKey
pubKeyId string
expectedAlgorithm Algorithm
expectErrorSigningResponse bool
} {
{
name : "want digest" ,
prefs : [ ] Algorithm { RSA_SHA512 } ,
2019-09-07 04:16:36 +09:00
digestAlg : DigestSha256 ,
2018-05-18 07:04:09 +09:00
headers : [ ] string { "Date" , "Digest" } ,
scheme : Signature ,
privKey : privKey ,
pubKeyId : "pubKeyId" ,
expectedAlgorithm : RSA_SHA512 ,
} ,
}
for _ , test := range failingTests {
2019-03-10 07:56:49 +09:00
t . Run ( test . name , func ( t * testing . T ) {
test := test
2019-09-07 04:16:36 +09:00
s , a , err := NewSigner ( test . prefs , test . digestAlg , test . headers , test . scheme )
2019-03-10 07:56:49 +09:00
if err != nil {
t . Fatalf ( "%s" , err )
}
if a != test . expectedAlgorithm {
t . Fatalf ( "got %s, want %s" , a , test . expectedAlgorithm )
}
resp := httptest . NewRecorder ( )
resp . HeaderMap . Set ( "Date" , testDate )
resp . HeaderMap . Set ( "Digest" , testDigest )
2019-09-07 04:16:36 +09:00
err = s . SignResponse ( test . privKey , test . pubKeyId , resp , nil )
2019-03-10 07:56:49 +09:00
if err != nil {
t . Fatalf ( "expected error, got nil" )
}
} )
2018-05-18 07:04:09 +09:00
}
}
func TestNewVerifier ( t * testing . T ) {
for _ , test := range tests {
2019-03-10 07:56:49 +09:00
t . Run ( test . name , func ( t * testing . T ) {
test := test
// Prepare
req , err := http . NewRequest ( testMethod , testUrl , nil )
if err != nil {
t . Fatalf ( "%s" , err )
}
req . Header . Set ( "Date" , testDate )
2019-09-07 04:16:36 +09:00
if test . body == nil {
req . Header . Set ( "Digest" , testDigest )
}
s , _ , err := NewSigner ( test . prefs , test . digestAlg , test . headers , test . scheme )
2019-03-10 07:56:49 +09:00
if err != nil {
t . Fatalf ( "%s" , err )
}
2019-09-07 04:16:36 +09:00
err = s . SignRequest ( test . privKey , test . pubKeyId , req , test . body )
2019-03-10 07:56:49 +09:00
if err != nil {
t . Fatalf ( "%s" , err )
}
// Test verification
v , err := NewVerifier ( req )
if err != nil {
t . Fatalf ( "%s" , err )
}
if v . KeyId ( ) != test . pubKeyId {
t . Fatalf ( "got %s, want %s" , v . KeyId ( ) , test . pubKeyId )
}
err = v . Verify ( test . pubKey , test . expectedAlgorithm )
if err != nil {
t . Fatalf ( "%s" , err )
}
} )
2018-05-18 07:04:09 +09:00
}
}
func TestNewResponseVerifier ( t * testing . T ) {
for _ , test := range tests {
2019-03-10 07:56:49 +09:00
t . Run ( test . name , func ( t * testing . T ) {
test := test
if test . expectErrorSigningResponse {
return
}
// Prepare
resp := httptest . NewRecorder ( )
resp . HeaderMap . Set ( "Date" , testDate )
2019-09-07 04:16:36 +09:00
if test . body == nil {
resp . HeaderMap . Set ( "Digest" , testDigest )
}
s , _ , err := NewSigner ( test . prefs , test . digestAlg , test . headers , test . scheme )
2019-03-10 07:56:49 +09:00
if err != nil {
t . Fatalf ( "%s" , err )
}
2019-09-07 04:16:36 +09:00
err = s . SignResponse ( test . privKey , test . pubKeyId , resp , test . body )
2019-03-10 07:56:49 +09:00
if err != nil {
t . Fatalf ( "%s" , err )
}
// Test verification
v , err := NewResponseVerifier ( resp . Result ( ) )
if err != nil {
t . Fatalf ( "%s" , err )
}
if v . KeyId ( ) != test . pubKeyId {
t . Fatalf ( "got %s, want %s" , v . KeyId ( ) , test . pubKeyId )
}
err = v . Verify ( test . pubKey , test . expectedAlgorithm )
if err != nil {
t . Fatalf ( "%s" , err )
}
} )
2018-05-18 07:04:09 +09:00
}
}
2019-03-10 17:56:13 +09:00
2019-09-05 07:01:59 +09:00
// Test_Signing_HTTP_Messages_AppendixC implement tests from Appendix C
// in the http signatures specification:
// https://tools.ietf.org/html/draft-cavage-http-signatures-10#appendix-C
2019-03-10 17:56:13 +09:00
func Test_Signing_HTTP_Messages_AppendixC ( t * testing . T ) {
specTests := [ ] struct {
name string
headers [ ] string
expectedSignature string
} {
{
2019-09-05 07:01:59 +09:00
name : "C.1. Default Test" ,
headers : [ ] string { } ,
// NOTE: In the Appendix C tests, the following is NOT included:
// `headers="date"`
// But httpsig will ALWAYS explicitly list the headers used in its
// signature. Hence, I have introduced it here.
//
// NOTE: In verification, if there are no headers listed, the
// default headers (date) are indeed used as required by the
// specification.
expectedSignature : ` Authorization: Signature keyId="Test",algorithm="rsa-sha256",headers="date",signature="SjWJWbWN7i0wzBvtPl8rbASWz5xQW6mcJmn+ibttBqtifLN7Sazz6m79cNfwwb8DMJ5cou1s7uEGKKCs+FLEEaDV5lp7q25WqS+lavg7T8hc0GppauB6hbgEKTwblDHYGEtbGmtdHgVCk9SuS13F0hZ8FD0k/5OxEPXe5WozsbM=" ` ,
2019-03-10 17:56:13 +09:00
} ,
{
name : "C.2. Basic Test" ,
headers : [ ] string { "(request-target)" , "host" , "date" } ,
expectedSignature : ` Authorization: Signature keyId="Test",algorithm="rsa-sha256",headers="(request-target) host date",signature="qdx+H7PHHDZgy4y/Ahn9Tny9V3GP6YgBPyUXMmoxWtLbHpUnXS2mg2+SbrQDMCJypxBLSPQR2aAjn7ndmw2iicw3HMbe8VfEdKFYRqzic+efkb3nndiv/x1xSHDJWeSWkx3ButlYSuBskLu6kd9Fswtemr3lgdDEmn04swr2Os0=" ` ,
} ,
{
name : "C.3. All Headers Test" ,
headers : [ ] string { "(request-target)" , "host" , "date" , "content-type" , "digest" , "content-length" } ,
expectedSignature : ` Authorization: Signature keyId="Test",algorithm="rsa-sha256",headers="(request-target) host date content-type digest content-length",signature="vSdrb+dS3EceC9bcwHSo4MlyKS59iFIrhgYkz8+oVLEEzmYZZvRs8rgOp+63LEM3v+MFHB32NfpB2bEKBIvB1q52LaEUHFv120V01IL+TAD48XaERZFukWgHoBTLMhYS2Gb51gWxpeIq8knRmPnYePbF5MOkR0Zkly4zKH7s1dE=" ` ,
} ,
}
for _ , test := range specTests {
t . Run ( test . name , func ( t * testing . T ) {
test := test
r , err := http . NewRequest ( "POST" , "http://example.com/foo?param=value&pet=dog" , bytes . NewBuffer ( [ ] byte ( testSpecBody ) ) )
if err != nil {
t . Fatalf ( "error creating request: %s" , err )
}
r . Header [ "Date" ] = [ ] string { testSpecDate }
r . Header [ "Host" ] = [ ] string { r . URL . Host }
r . Header [ "Content-Length" ] = [ ] string { strconv . Itoa ( len ( testSpecBody ) ) }
r . Header [ "Content-Type" ] = [ ] string { "application/json" }
setDigest ( r )
2019-09-07 04:16:36 +09:00
s , _ , err := NewSigner ( [ ] Algorithm { RSA_SHA256 } , DigestSha256 , test . headers , Authorization )
2019-03-10 17:56:13 +09:00
if err != nil {
t . Fatalf ( "error creating signer: %s" , err )
}
2019-09-07 04:16:36 +09:00
if err := s . SignRequest ( testSpecRSAPrivateKey , "Test" , r , nil ) ; err != nil {
2019-03-10 17:56:13 +09:00
t . Fatalf ( "error signing request: %s" , err )
}
expectedAuth := test . expectedSignature
gotAuth := fmt . Sprintf ( "Authorization: %s" , r . Header [ "Authorization" ] [ 0 ] )
if gotAuth != expectedAuth {
2019-09-05 07:01:59 +09:00
t . Errorf ( "Signature string mismatch\nGot: %s\nWant: %s" , gotAuth , expectedAuth )
}
} )
}
}
// Test_Verifying_HTTP_Messages_AppendixC implement tests from Appendix C
// in the http signatures specification:
// https://tools.ietf.org/html/draft-cavage-http-signatures-10#appendix-C
func Test_Verifying_HTTP_Messages_AppendixC ( t * testing . T ) {
specTests := [ ] struct {
name string
headers [ ] string
signature string
} {
{
name : "C.1. Default Test" ,
headers : [ ] string { } ,
signature : ` Signature keyId="Test",algorithm="rsa-sha256",signature="SjWJWbWN7i0wzBvtPl8rbASWz5xQW6mcJmn+ibttBqtifLN7Sazz6m79cNfwwb8DMJ5cou1s7uEGKKCs+FLEEaDV5lp7q25WqS+lavg7T8hc0GppauB6hbgEKTwblDHYGEtbGmtdHgVCk9SuS13F0hZ8FD0k/5OxEPXe5WozsbM=" ` ,
} ,
{
name : "C.2. Basic Test" ,
headers : [ ] string { "(request-target)" , "host" , "date" } ,
signature : ` Signature keyId="Test",algorithm="rsa-sha256",headers="(request-target) host date",signature="qdx+H7PHHDZgy4y/Ahn9Tny9V3GP6YgBPyUXMmoxWtLbHpUnXS2mg2+SbrQDMCJypxBLSPQR2aAjn7ndmw2iicw3HMbe8VfEdKFYRqzic+efkb3nndiv/x1xSHDJWeSWkx3ButlYSuBskLu6kd9Fswtemr3lgdDEmn04swr2Os0=" ` ,
} ,
{
name : "C.3. All Headers Test" ,
headers : [ ] string { "(request-target)" , "host" , "date" , "content-type" , "digest" , "content-length" } ,
signature : ` Signature keyId="Test",algorithm="rsa-sha256",headers="(request-target) host date content-type digest content-length",signature="vSdrb+dS3EceC9bcwHSo4MlyKS59iFIrhgYkz8+oVLEEzmYZZvRs8rgOp+63LEM3v+MFHB32NfpB2bEKBIvB1q52LaEUHFv120V01IL+TAD48XaERZFukWgHoBTLMhYS2Gb51gWxpeIq8knRmPnYePbF5MOkR0Zkly4zKH7s1dE=" ` ,
} ,
}
for _ , test := range specTests {
t . Run ( test . name , func ( t * testing . T ) {
test := test
r , err := http . NewRequest ( "POST" , "http://example.com/foo?param=value&pet=dog" , bytes . NewBuffer ( [ ] byte ( testSpecBody ) ) )
if err != nil {
t . Fatalf ( "error creating request: %s" , err )
}
r . Header [ "Date" ] = [ ] string { testSpecDate }
r . Header [ "Host" ] = [ ] string { r . URL . Host }
r . Header [ "Content-Length" ] = [ ] string { strconv . Itoa ( len ( testSpecBody ) ) }
r . Header [ "Content-Type" ] = [ ] string { "application/json" }
setDigest ( r )
r . Header [ "Authorization" ] = [ ] string { test . signature }
v , err := NewVerifier ( r )
if err != nil {
t . Fatalf ( "error creating verifier: %s" , err )
}
if "Test" != v . KeyId ( ) {
t . Errorf ( "KeyId mismatch\nGot: %s\nWant: Test" , v . KeyId ( ) )
}
if err := v . Verify ( testSpecRSAPublicKey , RSA_SHA256 ) ; err != nil {
t . Errorf ( "Verification failure: %s" , err )
2019-03-10 17:56:13 +09:00
}
} )
}
}
func loadPrivateKey ( keyData [ ] byte ) ( * rsa . PrivateKey , error ) {
pem , _ := pem . Decode ( keyData )
if pem . Type != "RSA PRIVATE KEY" {
return nil , fmt . Errorf ( "RSA private key is of the wrong type: %s" , pem . Type )
}
return x509 . ParsePKCS1PrivateKey ( pem . Bytes )
}
func loadPublicKey ( keyData [ ] byte ) ( * rsa . PublicKey , error ) {
pem , _ := pem . Decode ( keyData )
if pem . Type != "PUBLIC KEY" {
return nil , fmt . Errorf ( "public key is of the wrong type: %s" , pem . Type )
}
key , err := x509 . ParsePKIXPublicKey ( pem . Bytes )
if err != nil {
return nil , err
}
return key . ( * rsa . PublicKey ) , nil
}
func setDigest ( r * http . Request ) ( [ ] byte , error ) {
var bodyBytes [ ] byte
if _ , ok := r . Header [ "Digest" ] ; ! ok {
body := ""
if r . Body != nil {
var err error
bodyBytes , err = ioutil . ReadAll ( r . Body )
if err != nil {
return nil , fmt . Errorf ( "error reading body. %v" , err )
}
// And now set a new body, which will simulate the same data we read:
r . Body = ioutil . NopCloser ( bytes . NewBuffer ( bodyBytes ) )
body = string ( bodyBytes )
}
d := sha256 . Sum256 ( [ ] byte ( body ) )
r . Header [ "Digest" ] = [ ] string { fmt . Sprintf ( "SHA-256=%s" , base64 . StdEncoding . EncodeToString ( d [ : ] ) ) }
}
return bodyBytes , nil
}
const testSpecBody = ` { "hello": "world"} `
const testSpecDate = ` Sun, 05 Jan 2014 21:31:40 GMT `
const testSpecPrivateKeyPEM = ` -- -- - BEGIN RSA PRIVATE KEY -- -- -
MIICXgIBAAKBgQDCFENGw33yGihy92pDjZQhl0C36rPJj + CvfSC8 + q28hxA161QF
NUd13wuCTUcq0Qd2qsBe / 2 hFyc2DCJJg0h1L78 + 6 Z4UMR7EOcpfdUE9Hf3m / hs + F
UR45uBJeDK1HSFHD8bHKD6kv8FPGfJTotc + 2 xjJwoYi + 1 hqp1fIekaxsyQIDAQAB
AoGBAJR8ZkCUvx5kzv + utdl7T5MnordT1TvoXXJGXK7ZZ + UuvMNUCdN2QPc4sBiA
QWvLw1cSKt5DsKZ8UETpYPy8pPYnnDEz2dDYiaew9 + xEpubyeW2oH4Zx71wqBtOK
kqwrXa / pzdpiucRRjk6vE6YY7EBBs / g7uanVpGibOVAEsqH1AkEA7DkjVH28WDUg
f1nqvfn2Kj6CT7nIcE3jGJsZZ7zlZmBmHFDONMLUrXR / Zm3pR5m0tCmBqa5RK95u
412 jt1dPIwJBANJT3v8pnkth48bQo / fKel6uEYyboRtA5 / uHuHkZ6FQF7OUkGogc
mSJluOdc5t6hI1VsLn0QZEjQZMEOWr + wKSMCQQCC4kXJEsHAve77oP6HtG / IiEn7
kpyUXRNvFsDE0czpJJBvL / aRFUJxuRK91jhjC68sA7NsKMGg5OXb5I5Jj36xAkEA
gIT7aFOYBFwGgQAQkWNKLvySgKbAZRTeLBacpHMuQdl1DfdntvAyqpAZ0lY0RKmW
G6aFKaqQfOXKCyWoUiVknQJAXrlgySFci / 2 ueKlIE1QqIiLSZ8V8OlpFLRnb1pzI
7 U1yQXnTAEFYM560yJlzUpOb1V4cScGd365tiSMvxLOvTA ==
-- -- - END RSA PRIVATE KEY -- -- - `
const testSpecPublicKeyPEM = ` -- -- - BEGIN PUBLIC KEY -- -- -
MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDCFENGw33yGihy92pDjZQhl0C3
6 rPJj + CvfSC8 + q28hxA161QFNUd13wuCTUcq0Qd2qsBe / 2 hFyc2DCJJg0h1L78 + 6
Z4UMR7EOcpfdUE9Hf3m / hs + FUR45uBJeDK1HSFHD8bHKD6kv8FPGfJTotc + 2 xjJw
oYi + 1 hqp1fIekaxsyQIDAQAB
-- -- - END PUBLIC KEY -- -- - `