...

Source file src/gopkg.in/go-jose/go-jose.v2/jws.go

Documentation: gopkg.in/go-jose/go-jose.v2

     1  /*-
     2   * Copyright 2014 Square Inc.
     3   *
     4   * Licensed under the Apache License, Version 2.0 (the "License");
     5   * you may not use this file except in compliance with the License.
     6   * You may obtain a copy of the License at
     7   *
     8   *     http://www.apache.org/licenses/LICENSE-2.0
     9   *
    10   * Unless required by applicable law or agreed to in writing, software
    11   * distributed under the License is distributed on an "AS IS" BASIS,
    12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13   * See the License for the specific language governing permissions and
    14   * limitations under the License.
    15   */
    16  
    17  package jose
    18  
    19  import (
    20  	"bytes"
    21  	"encoding/base64"
    22  	"errors"
    23  	"fmt"
    24  	"strings"
    25  
    26  	"gopkg.in/go-jose/go-jose.v2/json"
    27  )
    28  
    29  // rawJSONWebSignature represents a raw JWS JSON object. Used for parsing/serializing.
    30  type rawJSONWebSignature struct {
    31  	Payload    *byteBuffer        `json:"payload,omitempty"`
    32  	Signatures []rawSignatureInfo `json:"signatures,omitempty"`
    33  	Protected  *byteBuffer        `json:"protected,omitempty"`
    34  	Header     *rawHeader         `json:"header,omitempty"`
    35  	Signature  *byteBuffer        `json:"signature,omitempty"`
    36  }
    37  
    38  // rawSignatureInfo represents a single JWS signature over the JWS payload and protected header.
    39  type rawSignatureInfo struct {
    40  	Protected *byteBuffer `json:"protected,omitempty"`
    41  	Header    *rawHeader  `json:"header,omitempty"`
    42  	Signature *byteBuffer `json:"signature,omitempty"`
    43  }
    44  
    45  // JSONWebSignature represents a signed JWS object after parsing.
    46  type JSONWebSignature struct {
    47  	payload []byte
    48  	// Signatures attached to this object (may be more than one for multi-sig).
    49  	// Be careful about accessing these directly, prefer to use Verify() or
    50  	// VerifyMulti() to ensure that the data you're getting is verified.
    51  	Signatures []Signature
    52  }
    53  
    54  // Signature represents a single signature over the JWS payload and protected header.
    55  type Signature struct {
    56  	// Merged header fields. Contains both protected and unprotected header
    57  	// values. Prefer using Protected and Unprotected fields instead of this.
    58  	// Values in this header may or may not have been signed and in general
    59  	// should not be trusted.
    60  	Header Header
    61  
    62  	// Protected header. Values in this header were signed and
    63  	// will be verified as part of the signature verification process.
    64  	Protected Header
    65  
    66  	// Unprotected header. Values in this header were not signed
    67  	// and in general should not be trusted.
    68  	Unprotected Header
    69  
    70  	// The actual signature value
    71  	Signature []byte
    72  
    73  	protected *rawHeader
    74  	header    *rawHeader
    75  	original  *rawSignatureInfo
    76  }
    77  
    78  // ParseSigned parses a signed message in compact or full serialization format.
    79  func ParseSigned(signature string) (*JSONWebSignature, error) {
    80  	signature = stripWhitespace(signature)
    81  	if strings.HasPrefix(signature, "{") {
    82  		return parseSignedFull(signature)
    83  	}
    84  
    85  	return parseSignedCompact(signature, nil)
    86  }
    87  
    88  // ParseDetached parses a signed message in compact serialization format with detached payload.
    89  func ParseDetached(signature string, payload []byte) (*JSONWebSignature, error) {
    90  	if payload == nil {
    91  		return nil, errors.New("go-jose/go-jose: nil payload")
    92  	}
    93  	return parseSignedCompact(stripWhitespace(signature), payload)
    94  }
    95  
    96  // Get a header value
    97  func (sig Signature) mergedHeaders() rawHeader {
    98  	out := rawHeader{}
    99  	out.merge(sig.protected)
   100  	out.merge(sig.header)
   101  	return out
   102  }
   103  
   104  // Compute data to be signed
   105  func (obj JSONWebSignature) computeAuthData(payload []byte, signature *Signature) ([]byte, error) {
   106  	var authData bytes.Buffer
   107  
   108  	protectedHeader := new(rawHeader)
   109  
   110  	if signature.original != nil && signature.original.Protected != nil {
   111  		if err := json.Unmarshal(signature.original.Protected.bytes(), protectedHeader); err != nil {
   112  			return nil, err
   113  		}
   114  		authData.WriteString(signature.original.Protected.base64())
   115  	} else if signature.protected != nil {
   116  		protectedHeader = signature.protected
   117  		authData.WriteString(base64.RawURLEncoding.EncodeToString(mustSerializeJSON(protectedHeader)))
   118  	}
   119  
   120  	needsBase64 := true
   121  
   122  	if protectedHeader != nil {
   123  		var err error
   124  		if needsBase64, err = protectedHeader.getB64(); err != nil {
   125  			needsBase64 = true
   126  		}
   127  	}
   128  
   129  	authData.WriteByte('.')
   130  
   131  	if needsBase64 {
   132  		authData.WriteString(base64.RawURLEncoding.EncodeToString(payload))
   133  	} else {
   134  		authData.Write(payload)
   135  	}
   136  
   137  	return authData.Bytes(), nil
   138  }
   139  
   140  // parseSignedFull parses a message in full format.
   141  func parseSignedFull(input string) (*JSONWebSignature, error) {
   142  	var parsed rawJSONWebSignature
   143  	err := json.Unmarshal([]byte(input), &parsed)
   144  	if err != nil {
   145  		return nil, err
   146  	}
   147  
   148  	return parsed.sanitized()
   149  }
   150  
   151  // sanitized produces a cleaned-up JWS object from the raw JSON.
   152  func (parsed *rawJSONWebSignature) sanitized() (*JSONWebSignature, error) {
   153  	if parsed.Payload == nil {
   154  		return nil, fmt.Errorf("go-jose/go-jose: missing payload in JWS message")
   155  	}
   156  
   157  	obj := &JSONWebSignature{
   158  		payload:    parsed.Payload.bytes(),
   159  		Signatures: make([]Signature, len(parsed.Signatures)),
   160  	}
   161  
   162  	if len(parsed.Signatures) == 0 {
   163  		// No signatures array, must be flattened serialization
   164  		signature := Signature{}
   165  		if parsed.Protected != nil && len(parsed.Protected.bytes()) > 0 {
   166  			signature.protected = &rawHeader{}
   167  			err := json.Unmarshal(parsed.Protected.bytes(), signature.protected)
   168  			if err != nil {
   169  				return nil, err
   170  			}
   171  		}
   172  
   173  		// Check that there is not a nonce in the unprotected header
   174  		if parsed.Header != nil && parsed.Header.getNonce() != "" {
   175  			return nil, ErrUnprotectedNonce
   176  		}
   177  
   178  		signature.header = parsed.Header
   179  		signature.Signature = parsed.Signature.bytes()
   180  		// Make a fake "original" rawSignatureInfo to store the unprocessed
   181  		// Protected header. This is necessary because the Protected header can
   182  		// contain arbitrary fields not registered as part of the spec. See
   183  		// https://tools.ietf.org/html/draft-ietf-jose-json-web-signature-41#section-4
   184  		// If we unmarshal Protected into a rawHeader with its explicit list of fields,
   185  		// we cannot marshal losslessly. So we have to keep around the original bytes.
   186  		// This is used in computeAuthData, which will first attempt to use
   187  		// the original bytes of a protected header, and fall back on marshaling the
   188  		// header struct only if those bytes are not available.
   189  		signature.original = &rawSignatureInfo{
   190  			Protected: parsed.Protected,
   191  			Header:    parsed.Header,
   192  			Signature: parsed.Signature,
   193  		}
   194  
   195  		var err error
   196  		signature.Header, err = signature.mergedHeaders().sanitized()
   197  		if err != nil {
   198  			return nil, err
   199  		}
   200  
   201  		if signature.header != nil {
   202  			signature.Unprotected, err = signature.header.sanitized()
   203  			if err != nil {
   204  				return nil, err
   205  			}
   206  		}
   207  
   208  		if signature.protected != nil {
   209  			signature.Protected, err = signature.protected.sanitized()
   210  			if err != nil {
   211  				return nil, err
   212  			}
   213  		}
   214  
   215  		// As per RFC 7515 Section 4.1.3, only public keys are allowed to be embedded.
   216  		jwk := signature.Header.JSONWebKey
   217  		if jwk != nil && (!jwk.Valid() || !jwk.IsPublic()) {
   218  			return nil, errors.New("go-jose/go-jose: invalid embedded jwk, must be public key")
   219  		}
   220  
   221  		obj.Signatures = append(obj.Signatures, signature)
   222  	}
   223  
   224  	for i, sig := range parsed.Signatures {
   225  		if sig.Protected != nil && len(sig.Protected.bytes()) > 0 {
   226  			obj.Signatures[i].protected = &rawHeader{}
   227  			err := json.Unmarshal(sig.Protected.bytes(), obj.Signatures[i].protected)
   228  			if err != nil {
   229  				return nil, err
   230  			}
   231  		}
   232  
   233  		// Check that there is not a nonce in the unprotected header
   234  		if sig.Header != nil && sig.Header.getNonce() != "" {
   235  			return nil, ErrUnprotectedNonce
   236  		}
   237  
   238  		var err error
   239  		obj.Signatures[i].Header, err = obj.Signatures[i].mergedHeaders().sanitized()
   240  		if err != nil {
   241  			return nil, err
   242  		}
   243  
   244  		if obj.Signatures[i].header != nil {
   245  			obj.Signatures[i].Unprotected, err = obj.Signatures[i].header.sanitized()
   246  			if err != nil {
   247  				return nil, err
   248  			}
   249  		}
   250  
   251  		if obj.Signatures[i].protected != nil {
   252  			obj.Signatures[i].Protected, err = obj.Signatures[i].protected.sanitized()
   253  			if err != nil {
   254  				return nil, err
   255  			}
   256  		}
   257  
   258  		obj.Signatures[i].Signature = sig.Signature.bytes()
   259  
   260  		// As per RFC 7515 Section 4.1.3, only public keys are allowed to be embedded.
   261  		jwk := obj.Signatures[i].Header.JSONWebKey
   262  		if jwk != nil && (!jwk.Valid() || !jwk.IsPublic()) {
   263  			return nil, errors.New("go-jose/go-jose: invalid embedded jwk, must be public key")
   264  		}
   265  
   266  		// Copy value of sig
   267  		original := sig
   268  
   269  		obj.Signatures[i].header = sig.Header
   270  		obj.Signatures[i].original = &original
   271  	}
   272  
   273  	return obj, nil
   274  }
   275  
   276  // parseSignedCompact parses a message in compact format.
   277  func parseSignedCompact(input string, payload []byte) (*JSONWebSignature, error) {
   278  	parts := strings.Split(input, ".")
   279  	if len(parts) != 3 {
   280  		return nil, fmt.Errorf("go-jose/go-jose: compact JWS format must have three parts")
   281  	}
   282  
   283  	if parts[1] != "" && payload != nil {
   284  		return nil, fmt.Errorf("go-jose/go-jose: payload is not detached")
   285  	}
   286  
   287  	rawProtected, err := base64.RawURLEncoding.DecodeString(parts[0])
   288  	if err != nil {
   289  		return nil, err
   290  	}
   291  
   292  	if payload == nil {
   293  		payload, err = base64.RawURLEncoding.DecodeString(parts[1])
   294  		if err != nil {
   295  			return nil, err
   296  		}
   297  	}
   298  
   299  	signature, err := base64.RawURLEncoding.DecodeString(parts[2])
   300  	if err != nil {
   301  		return nil, err
   302  	}
   303  
   304  	raw := &rawJSONWebSignature{
   305  		Payload:   newBuffer(payload),
   306  		Protected: newBuffer(rawProtected),
   307  		Signature: newBuffer(signature),
   308  	}
   309  	return raw.sanitized()
   310  }
   311  
   312  func (obj JSONWebSignature) compactSerialize(detached bool) (string, error) {
   313  	if len(obj.Signatures) != 1 || obj.Signatures[0].header != nil || obj.Signatures[0].protected == nil {
   314  		return "", ErrNotSupported
   315  	}
   316  
   317  	serializedProtected := base64.RawURLEncoding.EncodeToString(mustSerializeJSON(obj.Signatures[0].protected))
   318  	payload := ""
   319  	signature := base64.RawURLEncoding.EncodeToString(obj.Signatures[0].Signature)
   320  
   321  	if !detached {
   322  		payload = base64.RawURLEncoding.EncodeToString(obj.payload)
   323  	}
   324  
   325  	return fmt.Sprintf("%s.%s.%s", serializedProtected, payload, signature), nil
   326  }
   327  
   328  // CompactSerialize serializes an object using the compact serialization format.
   329  func (obj JSONWebSignature) CompactSerialize() (string, error) {
   330  	return obj.compactSerialize(false)
   331  }
   332  
   333  // DetachedCompactSerialize serializes an object using the compact serialization format with detached payload.
   334  func (obj JSONWebSignature) DetachedCompactSerialize() (string, error) {
   335  	return obj.compactSerialize(true)
   336  }
   337  
   338  // FullSerialize serializes an object using the full JSON serialization format.
   339  func (obj JSONWebSignature) FullSerialize() string {
   340  	raw := rawJSONWebSignature{
   341  		Payload: newBuffer(obj.payload),
   342  	}
   343  
   344  	if len(obj.Signatures) == 1 {
   345  		if obj.Signatures[0].protected != nil {
   346  			serializedProtected := mustSerializeJSON(obj.Signatures[0].protected)
   347  			raw.Protected = newBuffer(serializedProtected)
   348  		}
   349  		raw.Header = obj.Signatures[0].header
   350  		raw.Signature = newBuffer(obj.Signatures[0].Signature)
   351  	} else {
   352  		raw.Signatures = make([]rawSignatureInfo, len(obj.Signatures))
   353  		for i, signature := range obj.Signatures {
   354  			raw.Signatures[i] = rawSignatureInfo{
   355  				Header:    signature.header,
   356  				Signature: newBuffer(signature.Signature),
   357  			}
   358  
   359  			if signature.protected != nil {
   360  				raw.Signatures[i].Protected = newBuffer(mustSerializeJSON(signature.protected))
   361  			}
   362  		}
   363  	}
   364  
   365  	return string(mustSerializeJSON(raw))
   366  }
   367  

View as plain text