...

Source file src/gopkg.in/go-jose/go-jose.v2/signing.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  	"crypto/ecdsa"
    22  	"crypto/rsa"
    23  	"encoding/base64"
    24  	"errors"
    25  	"fmt"
    26  
    27  	"golang.org/x/crypto/ed25519"
    28  
    29  	"gopkg.in/go-jose/go-jose.v2/json"
    30  )
    31  
    32  // NonceSource represents a source of random nonces to go into JWS objects
    33  type NonceSource interface {
    34  	Nonce() (string, error)
    35  }
    36  
    37  // Signer represents a signer which takes a payload and produces a signed JWS object.
    38  type Signer interface {
    39  	Sign(payload []byte) (*JSONWebSignature, error)
    40  	Options() SignerOptions
    41  }
    42  
    43  // SigningKey represents an algorithm/key used to sign a message.
    44  type SigningKey struct {
    45  	Algorithm SignatureAlgorithm
    46  	Key       interface{}
    47  }
    48  
    49  // SignerOptions represents options that can be set when creating signers.
    50  type SignerOptions struct {
    51  	NonceSource NonceSource
    52  	EmbedJWK    bool
    53  
    54  	// Optional map of additional keys to be inserted into the protected header
    55  	// of a JWS object. Some specifications which make use of JWS like to insert
    56  	// additional values here. All values must be JSON-serializable.
    57  	ExtraHeaders map[HeaderKey]interface{}
    58  }
    59  
    60  // WithHeader adds an arbitrary value to the ExtraHeaders map, initializing it
    61  // if necessary. It returns itself and so can be used in a fluent style.
    62  func (so *SignerOptions) WithHeader(k HeaderKey, v interface{}) *SignerOptions {
    63  	if so.ExtraHeaders == nil {
    64  		so.ExtraHeaders = map[HeaderKey]interface{}{}
    65  	}
    66  	so.ExtraHeaders[k] = v
    67  	return so
    68  }
    69  
    70  // WithContentType adds a content type ("cty") header and returns the updated
    71  // SignerOptions.
    72  func (so *SignerOptions) WithContentType(contentType ContentType) *SignerOptions {
    73  	return so.WithHeader(HeaderContentType, contentType)
    74  }
    75  
    76  // WithType adds a type ("typ") header and returns the updated SignerOptions.
    77  func (so *SignerOptions) WithType(typ ContentType) *SignerOptions {
    78  	return so.WithHeader(HeaderType, typ)
    79  }
    80  
    81  // WithCritical adds the given names to the critical ("crit") header and returns
    82  // the updated SignerOptions.
    83  func (so *SignerOptions) WithCritical(names ...string) *SignerOptions {
    84  	if so.ExtraHeaders[headerCritical] == nil {
    85  		so.WithHeader(headerCritical, make([]string, 0, len(names)))
    86  	}
    87  	crit := so.ExtraHeaders[headerCritical].([]string)
    88  	so.ExtraHeaders[headerCritical] = append(crit, names...)
    89  	return so
    90  }
    91  
    92  // WithBase64 adds a base64url-encode payload ("b64") header and returns the updated
    93  // SignerOptions. When the "b64" value is "false", the payload is not base64 encoded.
    94  func (so *SignerOptions) WithBase64(b64 bool) *SignerOptions {
    95  	if !b64 {
    96  		so.WithHeader(headerB64, b64)
    97  		so.WithCritical(headerB64)
    98  	}
    99  	return so
   100  }
   101  
   102  type payloadSigner interface {
   103  	signPayload(payload []byte, alg SignatureAlgorithm) (Signature, error)
   104  }
   105  
   106  type payloadVerifier interface {
   107  	verifyPayload(payload []byte, signature []byte, alg SignatureAlgorithm) error
   108  }
   109  
   110  type genericSigner struct {
   111  	recipients   []recipientSigInfo
   112  	nonceSource  NonceSource
   113  	embedJWK     bool
   114  	extraHeaders map[HeaderKey]interface{}
   115  }
   116  
   117  type recipientSigInfo struct {
   118  	sigAlg    SignatureAlgorithm
   119  	publicKey func() *JSONWebKey
   120  	signer    payloadSigner
   121  }
   122  
   123  func staticPublicKey(jwk *JSONWebKey) func() *JSONWebKey {
   124  	return func() *JSONWebKey {
   125  		return jwk
   126  	}
   127  }
   128  
   129  // NewSigner creates an appropriate signer based on the key type
   130  func NewSigner(sig SigningKey, opts *SignerOptions) (Signer, error) {
   131  	return NewMultiSigner([]SigningKey{sig}, opts)
   132  }
   133  
   134  // NewMultiSigner creates a signer for multiple recipients
   135  func NewMultiSigner(sigs []SigningKey, opts *SignerOptions) (Signer, error) {
   136  	signer := &genericSigner{recipients: []recipientSigInfo{}}
   137  
   138  	if opts != nil {
   139  		signer.nonceSource = opts.NonceSource
   140  		signer.embedJWK = opts.EmbedJWK
   141  		signer.extraHeaders = opts.ExtraHeaders
   142  	}
   143  
   144  	for _, sig := range sigs {
   145  		err := signer.addRecipient(sig.Algorithm, sig.Key)
   146  		if err != nil {
   147  			return nil, err
   148  		}
   149  	}
   150  
   151  	return signer, nil
   152  }
   153  
   154  // newVerifier creates a verifier based on the key type
   155  func newVerifier(verificationKey interface{}) (payloadVerifier, error) {
   156  	switch verificationKey := verificationKey.(type) {
   157  	case ed25519.PublicKey:
   158  		return &edEncrypterVerifier{
   159  			publicKey: verificationKey,
   160  		}, nil
   161  	case *rsa.PublicKey:
   162  		return &rsaEncrypterVerifier{
   163  			publicKey: verificationKey,
   164  		}, nil
   165  	case *ecdsa.PublicKey:
   166  		return &ecEncrypterVerifier{
   167  			publicKey: verificationKey,
   168  		}, nil
   169  	case []byte:
   170  		return &symmetricMac{
   171  			key: verificationKey,
   172  		}, nil
   173  	case JSONWebKey:
   174  		return newVerifier(verificationKey.Key)
   175  	case *JSONWebKey:
   176  		return newVerifier(verificationKey.Key)
   177  	}
   178  	if ov, ok := verificationKey.(OpaqueVerifier); ok {
   179  		return &opaqueVerifier{verifier: ov}, nil
   180  	}
   181  	return nil, ErrUnsupportedKeyType
   182  }
   183  
   184  func (ctx *genericSigner) addRecipient(alg SignatureAlgorithm, signingKey interface{}) error {
   185  	recipient, err := makeJWSRecipient(alg, signingKey)
   186  	if err != nil {
   187  		return err
   188  	}
   189  
   190  	ctx.recipients = append(ctx.recipients, recipient)
   191  	return nil
   192  }
   193  
   194  func makeJWSRecipient(alg SignatureAlgorithm, signingKey interface{}) (recipientSigInfo, error) {
   195  	switch signingKey := signingKey.(type) {
   196  	case ed25519.PrivateKey:
   197  		return newEd25519Signer(alg, signingKey)
   198  	case *rsa.PrivateKey:
   199  		return newRSASigner(alg, signingKey)
   200  	case *ecdsa.PrivateKey:
   201  		return newECDSASigner(alg, signingKey)
   202  	case []byte:
   203  		return newSymmetricSigner(alg, signingKey)
   204  	case JSONWebKey:
   205  		return newJWKSigner(alg, signingKey)
   206  	case *JSONWebKey:
   207  		return newJWKSigner(alg, *signingKey)
   208  	}
   209  	if signer, ok := signingKey.(OpaqueSigner); ok {
   210  		return newOpaqueSigner(alg, signer)
   211  	}
   212  	return recipientSigInfo{}, ErrUnsupportedKeyType
   213  }
   214  
   215  func newJWKSigner(alg SignatureAlgorithm, signingKey JSONWebKey) (recipientSigInfo, error) {
   216  	recipient, err := makeJWSRecipient(alg, signingKey.Key)
   217  	if err != nil {
   218  		return recipientSigInfo{}, err
   219  	}
   220  	if recipient.publicKey != nil && recipient.publicKey() != nil {
   221  		// recipient.publicKey is a JWK synthesized for embedding when recipientSigInfo
   222  		// was created for the inner key (such as a RSA or ECDSA public key). It contains
   223  		// the pub key for embedding, but doesn't have extra params like key id.
   224  		publicKey := signingKey
   225  		publicKey.Key = recipient.publicKey().Key
   226  		recipient.publicKey = staticPublicKey(&publicKey)
   227  
   228  		// This should be impossible, but let's check anyway.
   229  		if !recipient.publicKey().IsPublic() {
   230  			return recipientSigInfo{}, errors.New("go-jose/go-jose: public key was unexpectedly not public")
   231  		}
   232  	}
   233  	return recipient, nil
   234  }
   235  
   236  func (ctx *genericSigner) Sign(payload []byte) (*JSONWebSignature, error) {
   237  	obj := &JSONWebSignature{}
   238  	obj.payload = payload
   239  	obj.Signatures = make([]Signature, len(ctx.recipients))
   240  
   241  	for i, recipient := range ctx.recipients {
   242  		protected := map[HeaderKey]interface{}{
   243  			headerAlgorithm: string(recipient.sigAlg),
   244  		}
   245  
   246  		if recipient.publicKey != nil && recipient.publicKey() != nil {
   247  			// We want to embed the JWK or set the kid header, but not both. Having a protected
   248  			// header that contains an embedded JWK while also simultaneously containing the kid
   249  			// header is confusing, and at least in ACME the two are considered to be mutually
   250  			// exclusive. The fact that both can exist at the same time is a somewhat unfortunate
   251  			// result of the JOSE spec. We've decided that this library will only include one or
   252  			// the other to avoid this confusion.
   253  			//
   254  			// See https://github.com/go-jose/go-jose/issues/157 for more context.
   255  			if ctx.embedJWK {
   256  				protected[headerJWK] = recipient.publicKey()
   257  			} else {
   258  				keyID := recipient.publicKey().KeyID
   259  				if keyID != "" {
   260  					protected[headerKeyID] = keyID
   261  				}
   262  			}
   263  		}
   264  
   265  		if ctx.nonceSource != nil {
   266  			nonce, err := ctx.nonceSource.Nonce()
   267  			if err != nil {
   268  				return nil, fmt.Errorf("go-jose/go-jose: Error generating nonce: %v", err)
   269  			}
   270  			protected[headerNonce] = nonce
   271  		}
   272  
   273  		for k, v := range ctx.extraHeaders {
   274  			protected[k] = v
   275  		}
   276  
   277  		serializedProtected := mustSerializeJSON(protected)
   278  		needsBase64 := true
   279  
   280  		if b64, ok := protected[headerB64]; ok {
   281  			if needsBase64, ok = b64.(bool); !ok {
   282  				return nil, errors.New("go-jose/go-jose: Invalid b64 header parameter")
   283  			}
   284  		}
   285  
   286  		var input bytes.Buffer
   287  
   288  		input.WriteString(base64.RawURLEncoding.EncodeToString(serializedProtected))
   289  		input.WriteByte('.')
   290  
   291  		if needsBase64 {
   292  			input.WriteString(base64.RawURLEncoding.EncodeToString(payload))
   293  		} else {
   294  			input.Write(payload)
   295  		}
   296  
   297  		signatureInfo, err := recipient.signer.signPayload(input.Bytes(), recipient.sigAlg)
   298  		if err != nil {
   299  			return nil, err
   300  		}
   301  
   302  		signatureInfo.protected = &rawHeader{}
   303  		for k, v := range protected {
   304  			b, err := json.Marshal(v)
   305  			if err != nil {
   306  				return nil, fmt.Errorf("go-jose/go-jose: Error marshalling item %#v: %v", k, err)
   307  			}
   308  			(*signatureInfo.protected)[k] = makeRawMessage(b)
   309  		}
   310  		obj.Signatures[i] = signatureInfo
   311  	}
   312  
   313  	return obj, nil
   314  }
   315  
   316  func (ctx *genericSigner) Options() SignerOptions {
   317  	return SignerOptions{
   318  		NonceSource:  ctx.nonceSource,
   319  		EmbedJWK:     ctx.embedJWK,
   320  		ExtraHeaders: ctx.extraHeaders,
   321  	}
   322  }
   323  
   324  // Verify validates the signature on the object and returns the payload.
   325  // This function does not support multi-signature, if you desire multi-sig
   326  // verification use VerifyMulti instead.
   327  //
   328  // Be careful when verifying signatures based on embedded JWKs inside the
   329  // payload header. You cannot assume that the key received in a payload is
   330  // trusted.
   331  func (obj JSONWebSignature) Verify(verificationKey interface{}) ([]byte, error) {
   332  	err := obj.DetachedVerify(obj.payload, verificationKey)
   333  	if err != nil {
   334  		return nil, err
   335  	}
   336  	return obj.payload, nil
   337  }
   338  
   339  // UnsafePayloadWithoutVerification returns the payload without
   340  // verifying it. The content returned from this function cannot be
   341  // trusted.
   342  func (obj JSONWebSignature) UnsafePayloadWithoutVerification() []byte {
   343  	return obj.payload
   344  }
   345  
   346  // DetachedVerify validates a detached signature on the given payload. In
   347  // most cases, you will probably want to use Verify instead. DetachedVerify
   348  // is only useful if you have a payload and signature that are separated from
   349  // each other.
   350  func (obj JSONWebSignature) DetachedVerify(payload []byte, verificationKey interface{}) error {
   351  	verifier, err := newVerifier(verificationKey)
   352  	if err != nil {
   353  		return err
   354  	}
   355  
   356  	if len(obj.Signatures) > 1 {
   357  		return errors.New("go-jose/go-jose: too many signatures in payload; expecting only one")
   358  	}
   359  
   360  	signature := obj.Signatures[0]
   361  	headers := signature.mergedHeaders()
   362  	critical, err := headers.getCritical()
   363  	if err != nil {
   364  		return err
   365  	}
   366  
   367  	for _, name := range critical {
   368  		if !supportedCritical[name] {
   369  			return ErrCryptoFailure
   370  		}
   371  	}
   372  
   373  	input, err := obj.computeAuthData(payload, &signature)
   374  	if err != nil {
   375  		return ErrCryptoFailure
   376  	}
   377  
   378  	alg := headers.getSignatureAlgorithm()
   379  	err = verifier.verifyPayload(input, signature.Signature, alg)
   380  	if err == nil {
   381  		return nil
   382  	}
   383  
   384  	return ErrCryptoFailure
   385  }
   386  
   387  // VerifyMulti validates (one of the multiple) signatures on the object and
   388  // returns the index of the signature that was verified, along with the signature
   389  // object and the payload. We return the signature and index to guarantee that
   390  // callers are getting the verified value.
   391  func (obj JSONWebSignature) VerifyMulti(verificationKey interface{}) (int, Signature, []byte, error) {
   392  	idx, sig, err := obj.DetachedVerifyMulti(obj.payload, verificationKey)
   393  	if err != nil {
   394  		return -1, Signature{}, nil, err
   395  	}
   396  	return idx, sig, obj.payload, nil
   397  }
   398  
   399  // DetachedVerifyMulti validates a detached signature on the given payload with
   400  // a signature/object that has potentially multiple signers. This returns the index
   401  // of the signature that was verified, along with the signature object. We return
   402  // the signature and index to guarantee that callers are getting the verified value.
   403  //
   404  // In most cases, you will probably want to use Verify or VerifyMulti instead.
   405  // DetachedVerifyMulti is only useful if you have a payload and signature that are
   406  // separated from each other, and the signature can have multiple signers at the
   407  // same time.
   408  func (obj JSONWebSignature) DetachedVerifyMulti(payload []byte, verificationKey interface{}) (int, Signature, error) {
   409  	verifier, err := newVerifier(verificationKey)
   410  	if err != nil {
   411  		return -1, Signature{}, err
   412  	}
   413  
   414  outer:
   415  	for i, signature := range obj.Signatures {
   416  		headers := signature.mergedHeaders()
   417  		critical, err := headers.getCritical()
   418  		if err != nil {
   419  			continue
   420  		}
   421  
   422  		for _, name := range critical {
   423  			if !supportedCritical[name] {
   424  				continue outer
   425  			}
   426  		}
   427  
   428  		input, err := obj.computeAuthData(payload, &signature)
   429  		if err != nil {
   430  			continue
   431  		}
   432  
   433  		alg := headers.getSignatureAlgorithm()
   434  		err = verifier.verifyPayload(input, signature.Signature, alg)
   435  		if err == nil {
   436  			return i, signature, nil
   437  		}
   438  	}
   439  
   440  	return -1, Signature{}, ErrCryptoFailure
   441  }
   442  

View as plain text