verifying.go (4744B)
1 package httpsig 2 3 import ( 4 "crypto" 5 "encoding/base64" 6 "errors" 7 "fmt" 8 "net/http" 9 "strconv" 10 "strings" 11 "time" 12 ) 13 14 var _ Verifier = &verifier{} 15 16 type verifier struct { 17 header http.Header 18 kId string 19 signature string 20 created int64 21 expires int64 22 headers []string 23 sigStringFn func(http.Header, []string, int64, int64) (string, error) 24 } 25 26 func newVerifier(h http.Header, sigStringFn func(http.Header, []string, int64, int64) (string, error)) (*verifier, error) { 27 scheme, s, err := getSignatureScheme(h) 28 if err != nil { 29 return nil, err 30 } 31 kId, sig, headers, created, expires, err := getSignatureComponents(scheme, s) 32 if created != 0 { 33 //check if created is not in the future, we assume a maximum clock offset of 10 seconds 34 now := time.Now().Unix() 35 if created-now > 10 { 36 return nil, errors.New("created is in the future") 37 } 38 } 39 if expires != 0 { 40 //check if expires is in the past, we assume a maximum clock offset of 10 seconds 41 now := time.Now().Unix() 42 if now-expires > 10 { 43 return nil, errors.New("signature expired") 44 } 45 } 46 if err != nil { 47 return nil, err 48 } 49 return &verifier{ 50 header: h, 51 kId: kId, 52 signature: sig, 53 created: created, 54 expires: expires, 55 headers: headers, 56 sigStringFn: sigStringFn, 57 }, nil 58 } 59 60 func (v *verifier) KeyId() string { 61 return v.kId 62 } 63 64 func (v *verifier) Verify(pKey crypto.PublicKey, algo Algorithm) error { 65 s, err := signerFromString(string(algo)) 66 if err == nil { 67 return v.asymmVerify(s, pKey) 68 } 69 m, err := macerFromString(string(algo)) 70 if err == nil { 71 return v.macVerify(m, pKey) 72 } 73 return fmt.Errorf("no crypto implementation available for %q", algo) 74 } 75 76 func (v *verifier) macVerify(m macer, pKey crypto.PublicKey) error { 77 key, ok := pKey.([]byte) 78 if !ok { 79 return fmt.Errorf("public key for MAC verifying must be of type []byte") 80 } 81 signature, err := v.sigStringFn(v.header, v.headers, v.created, v.expires) 82 if err != nil { 83 return err 84 } 85 actualMAC, err := base64.StdEncoding.DecodeString(v.signature) 86 if err != nil { 87 return err 88 } 89 ok, err = m.Equal([]byte(signature), actualMAC, key) 90 if err != nil { 91 return err 92 } else if !ok { 93 return fmt.Errorf("invalid http signature") 94 } 95 return nil 96 } 97 98 func (v *verifier) asymmVerify(s signer, pKey crypto.PublicKey) error { 99 toHash, err := v.sigStringFn(v.header, v.headers, v.created, v.expires) 100 if err != nil { 101 return err 102 } 103 signature, err := base64.StdEncoding.DecodeString(v.signature) 104 if err != nil { 105 return err 106 } 107 err = s.Verify(pKey, []byte(toHash), signature) 108 if err != nil { 109 return err 110 } 111 return nil 112 } 113 114 func getSignatureScheme(h http.Header) (scheme SignatureScheme, val string, err error) { 115 s := h.Get(string(Signature)) 116 sigHasAll := strings.Contains(s, keyIdParameter) || 117 strings.Contains(s, headersParameter) || 118 strings.Contains(s, signatureParameter) 119 a := h.Get(string(Authorization)) 120 authHasAll := strings.Contains(a, keyIdParameter) || 121 strings.Contains(a, headersParameter) || 122 strings.Contains(a, signatureParameter) 123 if sigHasAll && authHasAll { 124 err = fmt.Errorf("both %q and %q have signature parameters", Signature, Authorization) 125 return 126 } else if !sigHasAll && !authHasAll { 127 err = fmt.Errorf("neither %q nor %q have signature parameters", Signature, Authorization) 128 return 129 } else if sigHasAll { 130 val = s 131 scheme = Signature 132 return 133 } else { // authHasAll 134 val = a 135 scheme = Authorization 136 return 137 } 138 } 139 140 func getSignatureComponents(scheme SignatureScheme, s string) (kId, sig string, headers []string, created int64, expires int64, err error) { 141 if as := scheme.authScheme(); len(as) > 0 { 142 s = strings.TrimPrefix(s, as+prefixSeparater) 143 } 144 params := strings.Split(s, parameterSeparater) 145 for _, p := range params { 146 kv := strings.SplitN(p, parameterKVSeparater, 2) 147 if len(kv) != 2 { 148 err = fmt.Errorf("malformed http signature parameter: %v", kv) 149 return 150 } 151 k := kv[0] 152 v := strings.Trim(kv[1], parameterValueDelimiter) 153 switch k { 154 case keyIdParameter: 155 kId = v 156 case createdKey: 157 created, err = strconv.ParseInt(v, 10, 64) 158 if err != nil { 159 return 160 } 161 case expiresKey: 162 expires, err = strconv.ParseInt(v, 10, 64) 163 if err != nil { 164 return 165 } 166 case algorithmParameter: 167 // Deprecated, ignore 168 case headersParameter: 169 headers = strings.Split(v, headerParameterValueDelim) 170 case signatureParameter: 171 sig = v 172 default: 173 // Ignore unrecognized parameters 174 } 175 } 176 if len(kId) == 0 { 177 err = fmt.Errorf("missing %q parameter in http signature", keyIdParameter) 178 } else if len(sig) == 0 { 179 err = fmt.Errorf("missing %q parameter in http signature", signatureParameter) 180 } else if len(headers) == 0 { // Optional 181 headers = defaultHeaders 182 } 183 return 184 }