github.com/trustbloc/kms-go@v1.1.2/doc/jose/jws.go (about) 1 /* 2 Copyright SecureKey Technologies Inc. All Rights Reserved. 3 4 SPDX-License-Identifier: Apache-2.0 5 */ 6 7 package jose 8 9 import ( 10 "encoding/base64" 11 "errors" 12 "fmt" 13 "strings" 14 15 "github.com/go-jose/go-jose/v3/json" 16 ) 17 18 const ( 19 jwsPartsCount = 3 20 jwsHeaderPart = 0 21 jwsPayloadPart = 1 22 jwsSignaturePart = 2 23 ) 24 25 // JSONWebSignature defines JSON Web Signature (https://tools.ietf.org/html/rfc7515) 26 type JSONWebSignature struct { 27 ProtectedHeaders Headers 28 UnprotectedHeaders Headers 29 Payload []byte 30 31 signature []byte 32 joseHeaders Headers 33 } 34 35 // SignatureVerifier makes verification of JSON Web Signature. 36 type SignatureVerifier interface { 37 // Verify verifies JWS based on the signing input. 38 Verify(joseHeaders Headers, payload, signingInput, signature []byte) error 39 } 40 41 // SignatureVerifierFunc is a function wrapper for SignatureVerifier. 42 type SignatureVerifierFunc func(joseHeaders Headers, payload, signingInput, signature []byte) error 43 44 // Verify verifies JWS signature. 45 func (s SignatureVerifierFunc) Verify(joseHeaders Headers, payload, signingInput, signature []byte) error { 46 return s(joseHeaders, payload, signingInput, signature) 47 } 48 49 // DefaultSigningInputVerifier is a SignatureVerifier that generates the signing input 50 // from the given headers and payload, instead of using the signing input parameter. 51 type DefaultSigningInputVerifier func(joseHeaders Headers, payload, signingInput, signature []byte) error 52 53 // Verify verifies JWS signature. 54 func (s DefaultSigningInputVerifier) Verify(joseHeaders Headers, payload, _, signature []byte) error { 55 signingInputData, err := signingInput(joseHeaders, "", payload) 56 if err != nil { 57 return err 58 } 59 60 return s(joseHeaders, payload, signingInputData, signature) 61 } 62 63 // CompositeAlgSigVerifier defines composite signature verifier based on the algorithm 64 // taken from JOSE header alg. 65 type CompositeAlgSigVerifier struct { 66 verifierByAlg map[string]SignatureVerifier 67 } 68 69 // AlgSignatureVerifier defines verifier for particular signature algorithm. 70 type AlgSignatureVerifier struct { 71 Alg string 72 Verifier SignatureVerifier 73 } 74 75 // NewCompositeAlgSigVerifier creates a new CompositeAlgSigVerifier. 76 func NewCompositeAlgSigVerifier(v AlgSignatureVerifier, vOther ...AlgSignatureVerifier) *CompositeAlgSigVerifier { 77 verifierByAlg := make(map[string]SignatureVerifier, 1+len(vOther)) 78 verifierByAlg[v.Alg] = v.Verifier 79 80 for _, v := range vOther { 81 verifierByAlg[v.Alg] = v.Verifier 82 } 83 84 return &CompositeAlgSigVerifier{ 85 verifierByAlg: verifierByAlg, 86 } 87 } 88 89 // Verify verifiers JWS signature. 90 func (v *CompositeAlgSigVerifier) Verify(joseHeaders Headers, payload, signingInput, signature []byte) error { 91 alg, ok := joseHeaders.Algorithm() 92 if !ok { 93 return errors.New("'alg' JOSE header is not present") 94 } 95 96 verifier, ok := v.verifierByAlg[alg] 97 if !ok { 98 return fmt.Errorf("no verifier found for %s algorithm", alg) 99 } 100 101 return verifier.Verify(joseHeaders, payload, signingInput, signature) 102 } 103 104 // Signer defines JWS Signer interface. It makes signing of data and provides custom JWS headers relevant to the signer. 105 type Signer interface { 106 // Sign signs. 107 Sign(data []byte) ([]byte, error) 108 109 // Headers provides JWS headers. "alg" header must be provided (see https://tools.ietf.org/html/rfc7515#section-4.1) 110 Headers() Headers 111 } 112 113 // NewJWS creates JSON Web Signature. 114 func NewJWS(protectedHeaders, unprotectedHeaders Headers, payload []byte, signer Signer) (*JSONWebSignature, error) { 115 headers := mergeHeaders(protectedHeaders, signer.Headers()) 116 jws := &JSONWebSignature{ 117 ProtectedHeaders: headers, 118 UnprotectedHeaders: unprotectedHeaders, 119 Payload: payload, 120 joseHeaders: headers, 121 } 122 123 signature, err := sign(jws.joseHeaders, payload, signer) 124 if err != nil { 125 return nil, fmt.Errorf("sign JWS: %w", err) 126 } 127 128 jws.signature = signature 129 130 return jws, nil 131 } 132 133 // SerializeCompact makes JWS Compact Serialization (https://tools.ietf.org/html/rfc7515#section-7.1) 134 func (s JSONWebSignature) SerializeCompact(detached bool) (string, error) { 135 byteHeaders, err := json.Marshal(s.joseHeaders) 136 if err != nil { 137 return "", fmt.Errorf("marshal JWS JOSE Headers: %w", err) 138 } 139 140 b64Headers := base64.RawURLEncoding.EncodeToString(byteHeaders) 141 142 b64Payload := "" 143 if !detached { 144 b64Payload = base64.RawURLEncoding.EncodeToString(s.Payload) 145 } 146 147 b64Signature := base64.RawURLEncoding.EncodeToString(s.signature) 148 149 return fmt.Sprintf("%s.%s.%s", 150 b64Headers, 151 b64Payload, 152 b64Signature), nil 153 } 154 155 // Signature returns a copy of JWS signature. 156 func (s JSONWebSignature) Signature() []byte { 157 if s.signature == nil { 158 return nil 159 } 160 161 sCopy := make([]byte, len(s.signature)) 162 copy(sCopy, s.signature) 163 164 return sCopy 165 } 166 167 func mergeHeaders(h1, h2 Headers) Headers { 168 h := make(Headers, len(h1)+len(h2)) 169 170 for k, v := range h2 { 171 h[k] = v 172 } 173 174 for k, v := range h1 { 175 h[k] = v 176 } 177 178 return h 179 } 180 181 func sign(joseHeaders Headers, payload []byte, signer Signer) ([]byte, error) { 182 err := checkJWSHeaders(joseHeaders) 183 if err != nil { 184 return nil, fmt.Errorf("check JOSE headers: %w", err) 185 } 186 187 sigInput, err := signingInput(joseHeaders, "", payload) 188 if err != nil { 189 return nil, fmt.Errorf("prepare JWS verification data: %w", err) 190 } 191 192 signature, err := signer.Sign(sigInput) 193 if err != nil { 194 return nil, fmt.Errorf("sign JWS verification data: %w", err) 195 } 196 197 return signature, nil 198 } 199 200 // jwsParseOpts holds options for the JWS Parsing. 201 type jwsParseOpts struct { 202 detachedPayload []byte 203 } 204 205 // JWSParseOpt is the JWS Parser option. 206 type JWSParseOpt func(opts *jwsParseOpts) 207 208 // WithJWSDetachedPayload option is for definition of JWS detached payload. 209 func WithJWSDetachedPayload(payload []byte) JWSParseOpt { 210 return func(opts *jwsParseOpts) { 211 opts.detachedPayload = payload 212 } 213 } 214 215 // ParseJWS parses serialized JWS. Currently only JWS Compact Serialization parsing is supported. 216 func ParseJWS(jws string, verifier SignatureVerifier, opts ...JWSParseOpt) (*JSONWebSignature, error) { 217 pOpts := &jwsParseOpts{} 218 219 for _, opt := range opts { 220 opt(pOpts) 221 } 222 223 if strings.HasPrefix(jws, "{") { 224 // TODO support JWS JSON serialization format 225 // https://github.com/hyperledger/aries-framework-go/issues/1331 226 return nil, errors.New("JWS JSON serialization is not supported") 227 } 228 229 return parseCompacted(jws, verifier, pOpts) 230 } 231 232 // IsCompactJWS checks weather input is a compact JWS (based on https://tools.ietf.org/html/rfc7516#section-9) 233 func IsCompactJWS(s string) bool { 234 parts := strings.Split(s, ".") 235 236 return len(parts) == jwsPartsCount 237 } 238 239 func parseCompacted(jwsCompact string, verifier SignatureVerifier, opts *jwsParseOpts) (*JSONWebSignature, error) { 240 parts := strings.Split(jwsCompact, ".") 241 if len(parts) != jwsPartsCount { 242 return nil, errors.New("invalid JWS compact format") 243 } 244 245 joseHeaders, err := parseCompactedHeaders(parts) 246 if err != nil { 247 return nil, err 248 } 249 250 payload, err := parseCompactedPayload(parts[jwsPayloadPart], opts) 251 if err != nil { 252 return nil, err 253 } 254 255 sInput, err := signingInput(joseHeaders, parts[jwsHeaderPart], payload) 256 if err != nil { 257 return nil, fmt.Errorf("build signing input: %w", err) 258 } 259 260 signature, err := base64.RawURLEncoding.DecodeString(parts[jwsSignaturePart]) 261 if err != nil { 262 return nil, fmt.Errorf("decode base64 signature: %w", err) 263 } 264 265 err = verifier.Verify(joseHeaders, payload, sInput, signature) 266 if err != nil { 267 return nil, err 268 } 269 270 return &JSONWebSignature{ 271 ProtectedHeaders: joseHeaders, 272 Payload: payload, 273 signature: signature, 274 joseHeaders: joseHeaders, 275 }, nil 276 } 277 278 func parseCompactedPayload(jwsPayload string, opts *jwsParseOpts) ([]byte, error) { 279 if len(opts.detachedPayload) > 0 { 280 return opts.detachedPayload, nil 281 } 282 283 payload, err := base64.RawURLEncoding.DecodeString(jwsPayload) 284 if err != nil { 285 return nil, fmt.Errorf("decode base64 payload: %w", err) 286 } 287 288 return payload, nil 289 } 290 291 func parseCompactedHeaders(parts []string) (Headers, error) { 292 headersBytes, err := base64.RawURLEncoding.DecodeString(parts[jwsHeaderPart]) 293 if err != nil { 294 return nil, fmt.Errorf("decode base64 header: %w", err) 295 } 296 297 var joseHeaders Headers 298 299 err = json.Unmarshal(headersBytes, &joseHeaders) 300 if err != nil { 301 return nil, fmt.Errorf("unmarshal JSON headers: %w", err) 302 } 303 304 err = checkJWSHeaders(joseHeaders) 305 if err != nil { 306 return nil, err 307 } 308 309 return joseHeaders, nil 310 } 311 312 func signingInput(headers Headers, header string, payload []byte) ([]byte, error) { 313 headersBytes, err := json.Marshal(headers) 314 if err != nil { 315 return nil, fmt.Errorf("serialize JWS headers: %w", err) 316 } 317 318 hBase64 := true 319 320 if b64, ok := headers[HeaderB64Payload]; ok { 321 if hBase64, ok = b64.(bool); !ok { 322 return nil, errors.New("invalid b64 header") 323 } 324 } 325 326 // Will pass original header string for validation 327 headersStr := header 328 329 if headersStr == "" { 330 headersStr = base64.RawURLEncoding.EncodeToString(headersBytes) 331 } 332 333 var payloadStr string 334 335 if hBase64 { 336 payloadStr = base64.RawURLEncoding.EncodeToString(payload) 337 } else { 338 payloadStr = string(payload) 339 } 340 341 return []byte(fmt.Sprintf("%s.%s", headersStr, payloadStr)), nil 342 } 343 344 func checkJWSHeaders(headers Headers) error { 345 if _, ok := headers[HeaderAlgorithm]; !ok { 346 return fmt.Errorf("%s JWS header is not defined", HeaderAlgorithm) 347 } 348 349 return nil 350 } 351 352 func convertMapToValue(vOriginToBeMap, vDest interface{}) error { 353 if _, ok := vOriginToBeMap.(map[string]interface{}); !ok { 354 return errors.New("expected value to be a map") 355 } 356 357 mBytes, err := json.Marshal(vOriginToBeMap) 358 if err != nil { 359 return err 360 } 361 362 return json.Unmarshal(mBytes, vDest) 363 }