...

Source file src/github.com/letsencrypt/boulder/wfe2/verify_test.go

Documentation: github.com/letsencrypt/boulder/wfe2

     1  package wfe2
     2  
     3  import (
     4  	"context"
     5  	"crypto"
     6  	"crypto/dsa"
     7  	"crypto/ecdsa"
     8  	"crypto/elliptic"
     9  	"crypto/rsa"
    10  	"fmt"
    11  	"net/http"
    12  	"os"
    13  	"strings"
    14  	"testing"
    15  
    16  	"github.com/letsencrypt/boulder/core"
    17  	corepb "github.com/letsencrypt/boulder/core/proto"
    18  	"github.com/letsencrypt/boulder/goodkey"
    19  	bgrpc "github.com/letsencrypt/boulder/grpc"
    20  	"github.com/letsencrypt/boulder/grpc/noncebalancer"
    21  	"github.com/letsencrypt/boulder/mocks"
    22  	noncepb "github.com/letsencrypt/boulder/nonce/proto"
    23  	"github.com/letsencrypt/boulder/probs"
    24  	sapb "github.com/letsencrypt/boulder/sa/proto"
    25  	"github.com/letsencrypt/boulder/test"
    26  	"github.com/letsencrypt/boulder/web"
    27  	"github.com/prometheus/client_golang/prometheus"
    28  
    29  	"google.golang.org/grpc"
    30  	"gopkg.in/go-jose/go-jose.v2"
    31  )
    32  
    33  // sigAlgForKey uses `signatureAlgorithmForKey` but fails immediately using the
    34  // testing object if the sig alg is unknown.
    35  func sigAlgForKey(t *testing.T, key interface{}) jose.SignatureAlgorithm {
    36  	var sigAlg jose.SignatureAlgorithm
    37  	var err error
    38  	// Gracefully handle the case where a non-pointer public key is given where
    39  	// sigAlgorithmForKey always wants a pointer. It may be tempting to try and do
    40  	// `sigAlgorithmForKey(&jose.JSONWebKey{Key: &key})` without a type switch but this produces
    41  	// `*interface {}` and not the desired `*rsa.PublicKey` or `*ecdsa.PublicKey`.
    42  	switch k := key.(type) {
    43  	case rsa.PublicKey:
    44  		sigAlg, err = sigAlgorithmForKey(&jose.JSONWebKey{Key: &k})
    45  	case ecdsa.PublicKey:
    46  		sigAlg, err = sigAlgorithmForKey(&jose.JSONWebKey{Key: &k})
    47  	default:
    48  		sigAlg, err = sigAlgorithmForKey(&jose.JSONWebKey{Key: k})
    49  	}
    50  	test.Assert(t, err == nil, fmt.Sprintf("Error getting signature algorithm for key %#v", key))
    51  	return sigAlg
    52  }
    53  
    54  // keyAlgForKey returns a JWK key algorithm based on the provided private key.
    55  // Only ECDSA and RSA private keys are supported.
    56  func keyAlgForKey(t *testing.T, key interface{}) string {
    57  	switch key.(type) {
    58  	case *rsa.PrivateKey, rsa.PrivateKey:
    59  		return "RSA"
    60  	case *ecdsa.PrivateKey, ecdsa.PrivateKey:
    61  		return "ECDSA"
    62  	}
    63  	t.Fatalf("Can't figure out keyAlgForKey: %#v", key)
    64  	return ""
    65  }
    66  
    67  // pubKeyForKey returns the public key of an RSA/ECDSA private key provided as
    68  // argument.
    69  func pubKeyForKey(t *testing.T, privKey interface{}) interface{} {
    70  	switch k := privKey.(type) {
    71  	case *rsa.PrivateKey:
    72  		return k.PublicKey
    73  	case *ecdsa.PrivateKey:
    74  		return k.PublicKey
    75  	}
    76  	t.Fatalf("Unable to get public key for private key %#v", privKey)
    77  	return nil
    78  }
    79  
    80  // requestSigner offers methods to sign requests that will be accepted by a
    81  // specific WFE in unittests. It is only valid for the lifetime of a single
    82  // unittest.
    83  type requestSigner struct {
    84  	t            *testing.T
    85  	nonceService jose.NonceSource
    86  }
    87  
    88  // embeddedJWK creates a JWS for a given request body with an embedded JWK
    89  // corresponding to the private key provided. The URL and nonce extra headers
    90  // are set based on the additional arguments. A computed JWS, the corresponding
    91  // embedded JWK and the JWS in serialized string form are returned.
    92  func (rs requestSigner) embeddedJWK(
    93  	privateKey interface{},
    94  	url string,
    95  	req string) (*jose.JSONWebSignature, *jose.JSONWebKey, string) {
    96  	// if no key is provided default to test1KeyPrivatePEM
    97  	var publicKey interface{}
    98  	if privateKey == nil {
    99  		signer := loadKey(rs.t, []byte(test1KeyPrivatePEM))
   100  		privateKey = signer
   101  		publicKey = signer.Public()
   102  	} else {
   103  		publicKey = pubKeyForKey(rs.t, privateKey)
   104  	}
   105  
   106  	signerKey := jose.SigningKey{
   107  		Key:       privateKey,
   108  		Algorithm: sigAlgForKey(rs.t, publicKey),
   109  	}
   110  
   111  	opts := &jose.SignerOptions{
   112  		NonceSource: rs.nonceService,
   113  		EmbedJWK:    true,
   114  	}
   115  	if url != "" {
   116  		opts.ExtraHeaders = map[jose.HeaderKey]interface{}{
   117  			"url": url,
   118  		}
   119  	}
   120  
   121  	signer, err := jose.NewSigner(signerKey, opts)
   122  	test.AssertNotError(rs.t, err, "Failed to make signer")
   123  
   124  	jws, err := signer.Sign([]byte(req))
   125  	test.AssertNotError(rs.t, err, "Failed to sign req")
   126  
   127  	body := jws.FullSerialize()
   128  	parsedJWS, err := jose.ParseSigned(body)
   129  	test.AssertNotError(rs.t, err, "Failed to parse generated JWS")
   130  
   131  	return parsedJWS, parsedJWS.Signatures[0].Header.JSONWebKey, body
   132  }
   133  
   134  // signRequestKeyID creates a JWS for a given request body with key ID specified
   135  // based on the ID number provided. The URL and nonce extra headers
   136  // are set based on the additional arguments. A computed JWS, the corresponding
   137  // embedded JWK and the JWS in serialized string form are returned.
   138  func (rs requestSigner) byKeyID(
   139  	keyID int64,
   140  	privateKey interface{},
   141  	url string,
   142  	req string) (*jose.JSONWebSignature, *jose.JSONWebKey, string) {
   143  	// if no key is provided default to test1KeyPrivatePEM
   144  	if privateKey == nil {
   145  		privateKey = loadKey(rs.t, []byte(test1KeyPrivatePEM))
   146  	}
   147  
   148  	jwk := &jose.JSONWebKey{
   149  		Key:       privateKey,
   150  		Algorithm: keyAlgForKey(rs.t, privateKey),
   151  		KeyID:     fmt.Sprintf("http://localhost/acme/acct/%d", keyID),
   152  	}
   153  
   154  	signerKey := jose.SigningKey{
   155  		Key:       jwk,
   156  		Algorithm: jose.RS256,
   157  	}
   158  
   159  	opts := &jose.SignerOptions{
   160  		NonceSource: rs.nonceService,
   161  		ExtraHeaders: map[jose.HeaderKey]interface{}{
   162  			"url": url,
   163  		},
   164  	}
   165  
   166  	signer, err := jose.NewSigner(signerKey, opts)
   167  	test.AssertNotError(rs.t, err, "Failed to make signer")
   168  	jws, err := signer.Sign([]byte(req))
   169  	test.AssertNotError(rs.t, err, "Failed to sign req")
   170  
   171  	body := jws.FullSerialize()
   172  	parsedJWS, err := jose.ParseSigned(body)
   173  	test.AssertNotError(rs.t, err, "Failed to parse generated JWS")
   174  
   175  	return parsedJWS, jwk, body
   176  }
   177  
   178  // missingNonce returns an otherwise well-signed request that is missing its
   179  // nonce.
   180  func (rs requestSigner) missingNonce() *jose.JSONWebSignature {
   181  	privateKey := loadKey(rs.t, []byte(test1KeyPrivatePEM))
   182  	jwk := &jose.JSONWebKey{
   183  		Key:       privateKey,
   184  		Algorithm: keyAlgForKey(rs.t, privateKey),
   185  		KeyID:     "http://localhost/acme/acct/1",
   186  	}
   187  	signerKey := jose.SigningKey{
   188  		Key:       jwk,
   189  		Algorithm: jose.RS256,
   190  	}
   191  
   192  	opts := &jose.SignerOptions{
   193  		ExtraHeaders: map[jose.HeaderKey]interface{}{
   194  			"url": "https://example.com/acme/foo",
   195  		},
   196  	}
   197  
   198  	signer, err := jose.NewSigner(signerKey, opts)
   199  	test.AssertNotError(rs.t, err, "Failed to make signer")
   200  	jws, err := signer.Sign([]byte(""))
   201  	test.AssertNotError(rs.t, err, "Failed to sign req")
   202  
   203  	return jws
   204  }
   205  
   206  // invalidNonce returns an otherwise well-signed request with an invalid nonce.
   207  func (rs requestSigner) invalidNonce() *jose.JSONWebSignature {
   208  	privateKey := loadKey(rs.t, []byte(test1KeyPrivatePEM))
   209  	jwk := &jose.JSONWebKey{
   210  		Key:       privateKey,
   211  		Algorithm: keyAlgForKey(rs.t, privateKey),
   212  		KeyID:     "http://localhost/acme/acct/1",
   213  	}
   214  	signerKey := jose.SigningKey{
   215  		Key:       jwk,
   216  		Algorithm: jose.RS256,
   217  	}
   218  
   219  	opts := &jose.SignerOptions{
   220  		NonceSource: badNonceProvider{},
   221  		ExtraHeaders: map[jose.HeaderKey]interface{}{
   222  			"url": "https://example.com/acme/foo",
   223  		},
   224  	}
   225  
   226  	signer, err := jose.NewSigner(signerKey, opts)
   227  	test.AssertNotError(rs.t, err, "Failed to make signer")
   228  	jws, err := signer.Sign([]byte(""))
   229  	test.AssertNotError(rs.t, err, "Failed to sign req")
   230  
   231  	body := jws.FullSerialize()
   232  	parsedJWS, err := jose.ParseSigned(body)
   233  	test.AssertNotError(rs.t, err, "Failed to parse generated JWS")
   234  
   235  	return parsedJWS
   236  }
   237  
   238  // malformedNonce returns an otherwise well-signed request with a malformed
   239  // nonce.
   240  func (rs requestSigner) malformedNonce() *jose.JSONWebSignature {
   241  	privateKey := loadKey(rs.t, []byte(test1KeyPrivatePEM))
   242  	jwk := &jose.JSONWebKey{
   243  		Key:       privateKey,
   244  		Algorithm: keyAlgForKey(rs.t, privateKey),
   245  		KeyID:     "http://localhost/acme/acct/1",
   246  	}
   247  	signerKey := jose.SigningKey{
   248  		Key:       jwk,
   249  		Algorithm: jose.RS256,
   250  	}
   251  
   252  	opts := &jose.SignerOptions{
   253  		NonceSource: badNonceProvider{malformed: true},
   254  		ExtraHeaders: map[jose.HeaderKey]interface{}{
   255  			"url": "https://example.com/acme/foo",
   256  		},
   257  	}
   258  
   259  	signer, err := jose.NewSigner(signerKey, opts)
   260  	test.AssertNotError(rs.t, err, "Failed to make signer")
   261  	jws, err := signer.Sign([]byte(""))
   262  	test.AssertNotError(rs.t, err, "Failed to sign req")
   263  
   264  	body := jws.FullSerialize()
   265  	parsedJWS, err := jose.ParseSigned(body)
   266  	test.AssertNotError(rs.t, err, "Failed to parse generated JWS")
   267  
   268  	return parsedJWS
   269  }
   270  
   271  // shortNonce returns an otherwise well-signed request with a nonce shorter than
   272  // the prefix length.
   273  func (rs requestSigner) shortNonce() *jose.JSONWebSignature {
   274  	privateKey := loadKey(rs.t, []byte(test1KeyPrivatePEM))
   275  	jwk := &jose.JSONWebKey{
   276  		Key:       privateKey,
   277  		Algorithm: keyAlgForKey(rs.t, privateKey),
   278  		KeyID:     "http://localhost/acme/acct/1",
   279  	}
   280  	signerKey := jose.SigningKey{
   281  		Key:       jwk,
   282  		Algorithm: jose.RS256,
   283  	}
   284  
   285  	opts := &jose.SignerOptions{
   286  		NonceSource: badNonceProvider{shortNonce: true},
   287  		ExtraHeaders: map[jose.HeaderKey]interface{}{
   288  			"url": "https://example.com/acme/foo",
   289  		},
   290  	}
   291  
   292  	signer, err := jose.NewSigner(signerKey, opts)
   293  	test.AssertNotError(rs.t, err, "Failed to make signer")
   294  	jws, err := signer.Sign([]byte(""))
   295  	test.AssertNotError(rs.t, err, "Failed to sign req")
   296  
   297  	body := jws.FullSerialize()
   298  	parsedJWS, err := jose.ParseSigned(body)
   299  	test.AssertNotError(rs.t, err, "Failed to parse generated JWS")
   300  
   301  	return parsedJWS
   302  }
   303  
   304  func TestRejectsNone(t *testing.T) {
   305  	noneJWSBody := `
   306  		{
   307  			"header": {
   308  				"alg": "none",
   309  				"jwk": {
   310  					"kty": "RSA",
   311  					"n": "vrjT",
   312  					"e": "AQAB"
   313  				}
   314  			},
   315  			"payload": "aGkK",
   316    		"signature": "ghTIjrhiRl2pQ09vAkUUBbF5KziJdhzOTB-okM9SPRzU8Hyj0W1H5JA1Zoc-A-LuJGNAtYYHWqMw1SeZbT0l9FHcbMPeWDaJNkHS9jz5_g_Oyol8vcrWur2GDtB2Jgw6APtZKrbuGATbrF7g41Wijk6Kk9GXDoCnlfOQOhHhsrFFcWlCPLG-03TtKD6EBBoVBhmlp8DRLs7YguWRZ6jWNaEX-1WiRntBmhLqoqQFtvZxCBw_PRuaRw_RZBd1x2_BNYqEdOmVNC43UHMSJg3y_3yrPo905ur09aUTscf-C_m4Sa4M0FuDKn3bQ_pFrtz-aCCq6rcTIyxYpDqNvHMT2Q"
   317  		}
   318  	`
   319  	noneJWS, err := jose.ParseSigned(noneJWSBody)
   320  	if err != nil {
   321  		t.Fatal("Unable to parse noneJWS")
   322  	}
   323  	noneJWK := noneJWS.Signatures[0].Header.JSONWebKey
   324  
   325  	err = checkAlgorithm(noneJWK, noneJWS.Signatures[0].Header)
   326  	if err == nil {
   327  		t.Fatalf("checkAlgorithm did not reject JWS with alg: 'none'")
   328  	}
   329  	if err.Error() != "JWS signature header contains unsupported algorithm \"none\", expected one of RS256, ES256, ES384 or ES512" {
   330  		t.Fatalf("checkAlgorithm rejected JWS with alg: 'none', but for wrong reason: %#v", err)
   331  	}
   332  }
   333  
   334  func TestRejectsHS256(t *testing.T) {
   335  	hs256JWSBody := `
   336  		{
   337  			"header": {
   338  				"alg": "HS256",
   339  				"jwk": {
   340  					"kty": "RSA",
   341  					"n": "vrjT",
   342  					"e": "AQAB"
   343  				}
   344  			},
   345  			"payload": "aGkK",
   346    		"signature": "ghTIjrhiRl2pQ09vAkUUBbF5KziJdhzOTB-okM9SPRzU8Hyj0W1H5JA1Zoc-A-LuJGNAtYYHWqMw1SeZbT0l9FHcbMPeWDaJNkHS9jz5_g_Oyol8vcrWur2GDtB2Jgw6APtZKrbuGATbrF7g41Wijk6Kk9GXDoCnlfOQOhHhsrFFcWlCPLG-03TtKD6EBBoVBhmlp8DRLs7YguWRZ6jWNaEX-1WiRntBmhLqoqQFtvZxCBw_PRuaRw_RZBd1x2_BNYqEdOmVNC43UHMSJg3y_3yrPo905ur09aUTscf-C_m4Sa4M0FuDKn3bQ_pFrtz-aCCq6rcTIyxYpDqNvHMT2Q"
   347  		}
   348  	`
   349  
   350  	hs256JWS, err := jose.ParseSigned(hs256JWSBody)
   351  	if err != nil {
   352  		t.Fatal("Unable to parse hs256JWSBody")
   353  	}
   354  	hs256JWK := hs256JWS.Signatures[0].Header.JSONWebKey
   355  
   356  	err = checkAlgorithm(hs256JWK, hs256JWS.Signatures[0].Header)
   357  	if err == nil {
   358  		t.Fatalf("checkAlgorithm did not reject JWS with alg: 'HS256'")
   359  	}
   360  	expected := "JWS signature header contains unsupported algorithm \"HS256\", expected one of RS256, ES256, ES384 or ES512"
   361  	if err.Error() != expected {
   362  		t.Fatalf("checkAlgorithm rejected JWS with alg: 'none', but for wrong reason: got %q, wanted %q", err.Error(), expected)
   363  	}
   364  }
   365  
   366  func TestCheckAlgorithm(t *testing.T) {
   367  	testCases := []struct {
   368  		key         jose.JSONWebKey
   369  		jws         jose.JSONWebSignature
   370  		expectedErr string
   371  	}{
   372  		{
   373  			jose.JSONWebKey{},
   374  			jose.JSONWebSignature{
   375  				Signatures: []jose.Signature{
   376  					{
   377  						Header: jose.Header{
   378  							Algorithm: "RS256",
   379  						},
   380  					},
   381  				},
   382  			},
   383  			"JWK contains unsupported key type (expected RSA, or ECDSA P-256, P-384, or P-521)",
   384  		},
   385  		{
   386  			jose.JSONWebKey{
   387  				Algorithm: "HS256",
   388  				Key:       &rsa.PublicKey{},
   389  			},
   390  			jose.JSONWebSignature{
   391  				Signatures: []jose.Signature{
   392  					{
   393  						Header: jose.Header{
   394  							Algorithm: "HS256",
   395  						},
   396  					},
   397  				},
   398  			},
   399  			"JWS signature header contains unsupported algorithm \"HS256\", expected one of RS256, ES256, ES384 or ES512",
   400  		},
   401  		{
   402  			jose.JSONWebKey{
   403  				Algorithm: "ES256",
   404  				Key:       &dsa.PublicKey{},
   405  			},
   406  			jose.JSONWebSignature{
   407  				Signatures: []jose.Signature{
   408  					{
   409  						Header: jose.Header{
   410  							Algorithm: "ES512",
   411  						},
   412  					},
   413  				},
   414  			},
   415  			"JWK contains unsupported key type (expected RSA, or ECDSA P-256, P-384, or P-521)",
   416  		},
   417  		{
   418  			jose.JSONWebKey{
   419  				Algorithm: "RS256",
   420  				Key:       &rsa.PublicKey{},
   421  			},
   422  			jose.JSONWebSignature{
   423  				Signatures: []jose.Signature{
   424  					{
   425  						Header: jose.Header{
   426  							Algorithm: "ES512",
   427  						},
   428  					},
   429  				},
   430  			},
   431  			"JWS signature header algorithm \"ES512\" does not match expected algorithm \"RS256\" for JWK",
   432  		},
   433  		{
   434  			jose.JSONWebKey{
   435  				Algorithm: "HS256",
   436  				Key:       &rsa.PublicKey{},
   437  			},
   438  			jose.JSONWebSignature{
   439  				Signatures: []jose.Signature{
   440  					{
   441  						Header: jose.Header{
   442  							Algorithm: "RS256",
   443  						},
   444  					},
   445  				},
   446  			},
   447  			"JWK key header algorithm \"HS256\" does not match expected algorithm \"RS256\" for JWK",
   448  		},
   449  	}
   450  	for i, tc := range testCases {
   451  		err := checkAlgorithm(&tc.key, tc.jws.Signatures[0].Header)
   452  		if tc.expectedErr != "" && err.Error() != tc.expectedErr {
   453  			t.Errorf("TestCheckAlgorithm %d: Expected %q, got %q", i, tc.expectedErr, err)
   454  		}
   455  	}
   456  }
   457  
   458  func TestCheckAlgorithmSuccess(t *testing.T) {
   459  	jwsRS256 := &jose.JSONWebSignature{
   460  		Signatures: []jose.Signature{
   461  			{
   462  				Header: jose.Header{
   463  					Algorithm: "RS256",
   464  				},
   465  			},
   466  		},
   467  	}
   468  	goodJSONWebKeyRS256 := &jose.JSONWebKey{
   469  		Algorithm: "RS256",
   470  		Key:       &rsa.PublicKey{},
   471  	}
   472  	err := checkAlgorithm(goodJSONWebKeyRS256, jwsRS256.Signatures[0].Header)
   473  	test.AssertNotError(t, err, "RS256 key: Expected nil error")
   474  
   475  	badJSONWebKeyRS256 := &jose.JSONWebKey{
   476  		Algorithm: "ObviouslyWrongButNotZeroValue",
   477  		Key:       &rsa.PublicKey{},
   478  	}
   479  	err = checkAlgorithm(badJSONWebKeyRS256, jwsRS256.Signatures[0].Header)
   480  	test.AssertError(t, err, "RS256 key: Expected nil error")
   481  	test.AssertContains(t, err.Error(), "JWK key header algorithm \"ObviouslyWrongButNotZeroValue\" does not match expected algorithm \"RS256\" for JWK")
   482  
   483  	jwsES256 := &jose.JSONWebSignature{
   484  		Signatures: []jose.Signature{
   485  			{
   486  				Header: jose.Header{
   487  					Algorithm: "ES256",
   488  				},
   489  			},
   490  		},
   491  	}
   492  	goodJSONWebKeyES256 := &jose.JSONWebKey{
   493  		Algorithm: "ES256",
   494  		Key: &ecdsa.PublicKey{
   495  			Curve: elliptic.P256(),
   496  		},
   497  	}
   498  	err = checkAlgorithm(goodJSONWebKeyES256, jwsES256.Signatures[0].Header)
   499  	test.AssertNotError(t, err, "ES256 key: Expected nil error")
   500  
   501  	badJSONWebKeyES256 := &jose.JSONWebKey{
   502  		Algorithm: "ObviouslyWrongButNotZeroValue",
   503  		Key: &ecdsa.PublicKey{
   504  			Curve: elliptic.P256(),
   505  		},
   506  	}
   507  	err = checkAlgorithm(badJSONWebKeyES256, jwsES256.Signatures[0].Header)
   508  	test.AssertError(t, err, "ES256 key: Expected nil error")
   509  	test.AssertContains(t, err.Error(), "JWK key header algorithm \"ObviouslyWrongButNotZeroValue\" does not match expected algorithm \"ES256\" for JWK")
   510  }
   511  
   512  func TestValidPOSTRequest(t *testing.T) {
   513  	wfe, _, _ := setupWFE(t)
   514  
   515  	dummyContentLength := []string{"pretty long, idk, maybe a nibble or two?"}
   516  
   517  	testCases := []struct {
   518  		Name               string
   519  		Headers            map[string][]string
   520  		Body               *string
   521  		HTTPStatus         int
   522  		ProblemDetail      string
   523  		ErrorStatType      string
   524  		EnforceContentType bool
   525  	}{
   526  		// POST requests without a Content-Length should produce a problem
   527  		{
   528  			Name:          "POST without a Content-Length header",
   529  			Headers:       nil,
   530  			HTTPStatus:    http.StatusLengthRequired,
   531  			ProblemDetail: "missing Content-Length header",
   532  			ErrorStatType: "ContentLengthRequired",
   533  		},
   534  		// POST requests with a Replay-Nonce header should produce a problem
   535  		{
   536  			Name: "POST with a Replay-Nonce HTTP header",
   537  			Headers: map[string][]string{
   538  				"Content-Length": dummyContentLength,
   539  				"Replay-Nonce":   {"ima-misplaced-nonce"},
   540  				"Content-Type":   {expectedJWSContentType},
   541  			},
   542  			HTTPStatus:    http.StatusBadRequest,
   543  			ProblemDetail: "HTTP requests should NOT contain Replay-Nonce header. Use JWS nonce field",
   544  			ErrorStatType: "ReplayNonceOutsideJWS",
   545  		},
   546  		// POST requests without a body should produce a problem
   547  		{
   548  			Name: "POST with an empty POST body",
   549  			Headers: map[string][]string{
   550  				"Content-Length": dummyContentLength,
   551  				"Content-Type":   {expectedJWSContentType},
   552  			},
   553  			HTTPStatus:    http.StatusBadRequest,
   554  			ProblemDetail: "No body on POST",
   555  			ErrorStatType: "NoPOSTBody",
   556  		},
   557  		{
   558  			Name: "POST without a Content-Type header",
   559  			Headers: map[string][]string{
   560  				"Content-Length": dummyContentLength,
   561  			},
   562  			HTTPStatus: http.StatusUnsupportedMediaType,
   563  			ProblemDetail: fmt.Sprintf(
   564  				"No Content-Type header on POST. Content-Type must be %q",
   565  				expectedJWSContentType),
   566  			ErrorStatType:      "NoContentType",
   567  			EnforceContentType: true,
   568  		},
   569  		{
   570  			Name: "POST with an invalid Content-Type header",
   571  			Headers: map[string][]string{
   572  				"Content-Length": dummyContentLength,
   573  				"Content-Type":   {"fresh.and.rare"},
   574  			},
   575  			HTTPStatus: http.StatusUnsupportedMediaType,
   576  			ProblemDetail: fmt.Sprintf(
   577  				"Invalid Content-Type header on POST. Content-Type must be %q",
   578  				expectedJWSContentType),
   579  			ErrorStatType:      "WrongContentType",
   580  			EnforceContentType: true,
   581  		},
   582  	}
   583  
   584  	for _, tc := range testCases {
   585  		input := &http.Request{
   586  			Method: "POST",
   587  			URL:    mustParseURL("/"),
   588  			Header: tc.Headers,
   589  		}
   590  		t.Run(tc.Name, func(t *testing.T) {
   591  			prob := wfe.validPOSTRequest(input)
   592  			test.Assert(t, prob != nil, "No error returned for invalid POST")
   593  			test.AssertEquals(t, prob.Type, probs.MalformedProblem)
   594  			test.AssertEquals(t, prob.HTTPStatus, tc.HTTPStatus)
   595  			test.AssertEquals(t, prob.Detail, tc.ProblemDetail)
   596  			test.AssertMetricWithLabelsEquals(
   597  				t, wfe.stats.httpErrorCount, prometheus.Labels{"type": tc.ErrorStatType}, 1)
   598  		})
   599  	}
   600  }
   601  
   602  func TestEnforceJWSAuthType(t *testing.T) {
   603  	wfe, _, signer := setupWFE(t)
   604  
   605  	testKeyIDJWS, _, _ := signer.byKeyID(1, nil, "", "")
   606  	testEmbeddedJWS, _, _ := signer.embeddedJWK(nil, "", "")
   607  
   608  	// A hand crafted JWS that has both a Key ID and an embedded JWK
   609  	conflictJWSBody := `
   610  {
   611    "header": {
   612      "alg": "RS256", 
   613      "jwk": {
   614        "e": "AQAB", 
   615        "kty": "RSA", 
   616        "n": "ppbqGaMFnnq9TeMUryR6WW4Lr5WMgp46KlBXZkNaGDNQoifWt6LheeR5j9MgYkIFU7Z8Jw5-bpJzuBeEVwb-yHGh4Umwo_qKtvAJd44iLjBmhBSxq-OSe6P5hX1LGCByEZlYCyoy98zOtio8VK_XyS5VoOXqchCzBXYf32ksVUTrtH1jSlamKHGz0Q0pRKIsA2fLqkE_MD3jP6wUDD6ExMw_tKYLx21lGcK41WSrRpDH-kcZo1QdgCy2ceNzaliBX1eHmKG0-H8tY4tPQudk-oHQmWTdvUIiHO6gSKMGDZNWv6bq74VTCsRfUEAkuWhqUhgRSGzlvlZ24wjHv5Qdlw"
   617      }
   618    }, 
   619    "protected": "eyJub25jZSI6ICJibTl1WTJVIiwgInVybCI6ICJodHRwOi8vbG9jYWxob3N0L3Rlc3QiLCAia2lkIjogInRlc3RrZXkifQ", 
   620    "payload": "Zm9v", 
   621    "signature": "ghTIjrhiRl2pQ09vAkUUBbF5KziJdhzOTB-okM9SPRzU8Hyj0W1H5JA1Zoc-A-LuJGNAtYYHWqMw1SeZbT0l9FHcbMPeWDaJNkHS9jz5_g_Oyol8vcrWur2GDtB2Jgw6APtZKrbuGATbrF7g41Wijk6Kk9GXDoCnlfOQOhHhsrFFcWlCPLG-03TtKD6EBBoVBhmlp8DRLs7YguWRZ6jWNaEX-1WiRntBmhLqoqQFtvZxCBw_PRuaRw_RZBd1x2_BNYqEdOmVNC43UHMSJg3y_3yrPo905ur09aUTscf-C_m4Sa4M0FuDKn3bQ_pFrtz-aCCq6rcTIyxYpDqNvHMT2Q"
   622  }
   623  `
   624  
   625  	conflictJWS, err := jose.ParseSigned(conflictJWSBody)
   626  	if err != nil {
   627  		t.Fatal("Unable to parse conflict JWS")
   628  	}
   629  
   630  	testCases := []struct {
   631  		Name             string
   632  		JWS              *jose.JSONWebSignature
   633  		ExpectedAuthType jwsAuthType
   634  		ExpectedResult   *probs.ProblemDetails
   635  		ErrorStatType    string
   636  	}{
   637  		{
   638  			Name:             "Key ID and embedded JWS",
   639  			JWS:              conflictJWS,
   640  			ExpectedAuthType: invalidAuthType,
   641  			ExpectedResult: &probs.ProblemDetails{
   642  				Type:       probs.MalformedProblem,
   643  				Detail:     "jwk and kid header fields are mutually exclusive",
   644  				HTTPStatus: http.StatusBadRequest,
   645  			},
   646  			ErrorStatType: "JWSAuthTypeInvalid",
   647  		},
   648  		{
   649  			Name:             "Key ID when expected is embedded JWK",
   650  			JWS:              testKeyIDJWS,
   651  			ExpectedAuthType: embeddedJWK,
   652  			ExpectedResult: &probs.ProblemDetails{
   653  				Type:       probs.MalformedProblem,
   654  				Detail:     "No embedded JWK in JWS header",
   655  				HTTPStatus: http.StatusBadRequest,
   656  			},
   657  			ErrorStatType: "JWSAuthTypeWrong",
   658  		},
   659  		{
   660  			Name:             "Embedded JWK when expected is Key ID",
   661  			JWS:              testEmbeddedJWS,
   662  			ExpectedAuthType: embeddedKeyID,
   663  			ExpectedResult: &probs.ProblemDetails{
   664  				Type:       probs.MalformedProblem,
   665  				Detail:     "No Key ID in JWS header",
   666  				HTTPStatus: http.StatusBadRequest,
   667  			},
   668  			ErrorStatType: "JWSAuthTypeWrong",
   669  		},
   670  		{
   671  			Name:             "Key ID when expected is KeyID",
   672  			JWS:              testKeyIDJWS,
   673  			ExpectedAuthType: embeddedKeyID,
   674  			ExpectedResult:   nil,
   675  		},
   676  		{
   677  			Name:             "Embedded JWK when expected is embedded JWK",
   678  			JWS:              testEmbeddedJWS,
   679  			ExpectedAuthType: embeddedJWK,
   680  			ExpectedResult:   nil,
   681  		},
   682  	}
   683  
   684  	for _, tc := range testCases {
   685  		t.Run(tc.Name, func(t *testing.T) {
   686  			wfe.stats.joseErrorCount.Reset()
   687  			prob := wfe.enforceJWSAuthType(tc.JWS.Signatures[0].Header, tc.ExpectedAuthType)
   688  			if tc.ExpectedResult == nil && prob != nil {
   689  				t.Fatalf("Expected nil result, got %#v", prob)
   690  			} else {
   691  				test.AssertMarshaledEquals(t, prob, tc.ExpectedResult)
   692  			}
   693  			if tc.ErrorStatType != "" {
   694  				test.AssertMetricWithLabelsEquals(
   695  					t, wfe.stats.joseErrorCount, prometheus.Labels{"type": tc.ErrorStatType}, 1)
   696  			}
   697  		})
   698  	}
   699  }
   700  
   701  type badNonceProvider struct {
   702  	malformed  bool
   703  	shortNonce bool
   704  }
   705  
   706  func (b badNonceProvider) Nonce() (string, error) {
   707  	if b.malformed {
   708  		return "im-a-nonce", nil
   709  	}
   710  	if b.shortNonce {
   711  		// A nonce length of 4 is considered "short" because there is no nonce
   712  		// material to be redeemed after the prefix. Derived prefixes are 8
   713  		// characters and static prefixes are 4 characters.
   714  		return "woww", nil
   715  	}
   716  	if os.Getenv("BOULDER_CONFIG_DIR") != "test/config-next" {
   717  		// TODO(#6610): Remove this.
   718  		return "mlol3ov77I5Ui-cdaY_k8IcjK58FvbG0y_BCRrx5rGQ8rjA", nil
   719  	}
   720  	return "mlolmlol3ov77I5Ui-cdaY_k8IcjK58FvbG0y_BCRrx5rGQ8rjA", nil
   721  }
   722  
   723  func TestValidNonce(t *testing.T) {
   724  	wfe, _, signer := setupWFE(t)
   725  
   726  	goodJWS, _, _ := signer.embeddedJWK(nil, "", "")
   727  
   728  	testCases := []struct {
   729  		Name           string
   730  		JWS            *jose.JSONWebSignature
   731  		ExpectedResult *probs.ProblemDetails
   732  		ErrorStatType  string
   733  		// TODO(#6610): Remove this.
   734  		SkipConfigNext bool
   735  		// TODO(#6610): Remove this.
   736  		SkipConfig bool
   737  	}{
   738  		{
   739  			Name: "No nonce in JWS",
   740  			JWS:  signer.missingNonce(),
   741  			ExpectedResult: &probs.ProblemDetails{
   742  				Type:       probs.BadNonceProblem,
   743  				Detail:     "JWS has no anti-replay nonce",
   744  				HTTPStatus: http.StatusBadRequest,
   745  			},
   746  			ErrorStatType: "JWSMissingNonce",
   747  		},
   748  		{
   749  			Name: "Malformed nonce in JWS",
   750  			JWS:  signer.malformedNonce(),
   751  			ExpectedResult: &probs.ProblemDetails{
   752  				Type:       probs.BadNonceProblem,
   753  				Detail:     "JWS has an invalid anti-replay nonce: \"im-a-nonce\"",
   754  				HTTPStatus: http.StatusBadRequest,
   755  			},
   756  			ErrorStatType: "JWSMalformedNonce",
   757  		},
   758  		{
   759  			Name: "Canned nonce shorter than prefixLength in JWS",
   760  			JWS:  signer.shortNonce(),
   761  			ExpectedResult: &probs.ProblemDetails{
   762  				Type:       probs.BadNonceProblem,
   763  				Detail:     "JWS has an invalid anti-replay nonce: \"woww\"",
   764  				HTTPStatus: http.StatusBadRequest,
   765  			},
   766  			ErrorStatType: "JWSMalformedNonce",
   767  		},
   768  		{
   769  			Name: "Invalid nonce in JWS (test/config)",
   770  			JWS:  signer.invalidNonce(),
   771  			ExpectedResult: &probs.ProblemDetails{
   772  				Type:       probs.BadNonceProblem,
   773  				Detail:     "JWS has an invalid anti-replay nonce: \"mlol3ov77I5Ui-cdaY_k8IcjK58FvbG0y_BCRrx5rGQ8rjA\"",
   774  				HTTPStatus: http.StatusBadRequest,
   775  			},
   776  			ErrorStatType:  "JWSInvalidNonce",
   777  			SkipConfigNext: true,
   778  		},
   779  		{
   780  			Name: "Invalid nonce in JWS (test/config-next)",
   781  			JWS:  signer.invalidNonce(),
   782  			ExpectedResult: &probs.ProblemDetails{
   783  				Type:       probs.BadNonceProblem,
   784  				Detail:     "JWS has an invalid anti-replay nonce: \"mlolmlol3ov77I5Ui-cdaY_k8IcjK58FvbG0y_BCRrx5rGQ8rjA\"",
   785  				HTTPStatus: http.StatusBadRequest,
   786  			},
   787  			ErrorStatType: "JWSInvalidNonce",
   788  			SkipConfig:    true,
   789  		},
   790  		{
   791  			Name:           "Valid nonce in JWS",
   792  			JWS:            goodJWS,
   793  			ExpectedResult: nil,
   794  		},
   795  	}
   796  
   797  	for _, tc := range testCases {
   798  		t.Run(tc.Name, func(t *testing.T) {
   799  			// TODO(#6610): Remove this.
   800  			if os.Getenv("BOULDER_CONFIG_DIR") == "test/config-next" {
   801  				if tc.SkipConfigNext {
   802  					t.Skip("Skipping test in config-next")
   803  				}
   804  			} else if tc.SkipConfig {
   805  				t.Skip("Skipping test in config")
   806  			}
   807  			wfe.stats.joseErrorCount.Reset()
   808  			prob := wfe.validNonce(context.Background(), tc.JWS.Signatures[0].Header)
   809  			if tc.ExpectedResult == nil && prob != nil {
   810  				t.Fatalf("Expected nil result, got %#v", prob)
   811  			} else {
   812  				test.AssertMarshaledEquals(t, prob, tc.ExpectedResult)
   813  			}
   814  			if tc.ErrorStatType != "" {
   815  				test.AssertMetricWithLabelsEquals(
   816  					t, wfe.stats.joseErrorCount, prometheus.Labels{"type": tc.ErrorStatType}, 1)
   817  			}
   818  		})
   819  	}
   820  }
   821  
   822  // noBackendsNonceRedeemer is a nonce redeemer that always returns an error
   823  // indicating that the prefix matches no known nonce provider.
   824  type noBackendsNonceRedeemer struct{}
   825  
   826  func (n noBackendsNonceRedeemer) Redeem(ctx context.Context, _ *noncepb.NonceMessage, opts ...grpc.CallOption) (*noncepb.ValidMessage, error) {
   827  	return nil, noncebalancer.ErrNoBackendsMatchPrefix.Err()
   828  }
   829  
   830  func TestValidNonce_NoMatchingBackendFound(t *testing.T) {
   831  	// TODO(#6610): Remove this.
   832  	if os.Getenv("BOULDER_CONFIG_DIR") != "test/config-next" {
   833  		t.Skip("Skipping test in config")
   834  	}
   835  	wfe, _, signer := setupWFE(t)
   836  	goodJWS, _, _ := signer.embeddedJWK(nil, "", "")
   837  	wfe.rnc = noBackendsNonceRedeemer{}
   838  
   839  	// A valid JWS with a nonce whose prefix matches no known nonce provider should
   840  	// result in a BadNonceProblem.
   841  	prob := wfe.validNonce(context.Background(), goodJWS.Signatures[0].Header)
   842  	test.Assert(t, prob != nil, "Expected error for valid nonce with no backend")
   843  	test.AssertEquals(t, prob.Type, probs.BadNonceProblem)
   844  	test.AssertEquals(t, prob.HTTPStatus, http.StatusBadRequest)
   845  	test.AssertContains(t, prob.Detail, "JWS has an invalid anti-replay nonce")
   846  	test.AssertMetricWithLabelsEquals(t, wfe.stats.nonceNoMatchingBackendCount, prometheus.Labels{}, 1)
   847  }
   848  
   849  func (rs requestSigner) signExtraHeaders(
   850  	headers map[jose.HeaderKey]interface{}) (*jose.JSONWebSignature, string) {
   851  	privateKey := loadKey(rs.t, []byte(test1KeyPrivatePEM))
   852  
   853  	signerKey := jose.SigningKey{
   854  		Key:       privateKey,
   855  		Algorithm: sigAlgForKey(rs.t, privateKey.Public()),
   856  	}
   857  
   858  	opts := &jose.SignerOptions{
   859  		NonceSource:  rs.nonceService,
   860  		EmbedJWK:     true,
   861  		ExtraHeaders: headers,
   862  	}
   863  
   864  	signer, err := jose.NewSigner(signerKey, opts)
   865  	test.AssertNotError(rs.t, err, "Failed to make signer")
   866  
   867  	jws, err := signer.Sign([]byte(""))
   868  	test.AssertNotError(rs.t, err, "Failed to sign req")
   869  
   870  	body := jws.FullSerialize()
   871  	parsedJWS, err := jose.ParseSigned(body)
   872  	test.AssertNotError(rs.t, err, "Failed to parse generated JWS")
   873  
   874  	return parsedJWS, body
   875  }
   876  
   877  func TestValidPOSTURL(t *testing.T) {
   878  	wfe, _, signer := setupWFE(t)
   879  
   880  	// A JWS and HTTP request with no extra headers
   881  	noHeadersJWS, noHeadersJWSBody := signer.signExtraHeaders(nil)
   882  	noHeadersRequest := makePostRequestWithPath("test-path", noHeadersJWSBody)
   883  
   884  	// A JWS and HTTP request with extra headers, but no "url" extra header
   885  	noURLHeaders := map[jose.HeaderKey]interface{}{
   886  		"nifty": "swell",
   887  	}
   888  	noURLHeaderJWS, noURLHeaderJWSBody := signer.signExtraHeaders(noURLHeaders)
   889  	noURLHeaderRequest := makePostRequestWithPath("test-path", noURLHeaderJWSBody)
   890  
   891  	// A JWS and HTTP request with a mismatched HTTP URL to JWS "url" header
   892  	wrongURLHeaders := map[jose.HeaderKey]interface{}{
   893  		"url": "foobar",
   894  	}
   895  	wrongURLHeaderJWS, wrongURLHeaderJWSBody := signer.signExtraHeaders(wrongURLHeaders)
   896  	wrongURLHeaderRequest := makePostRequestWithPath("test-path", wrongURLHeaderJWSBody)
   897  
   898  	correctURLHeaderJWS, _, correctURLHeaderJWSBody := signer.embeddedJWK(nil, "http://localhost/test-path", "")
   899  	correctURLHeaderRequest := makePostRequestWithPath("test-path", correctURLHeaderJWSBody)
   900  
   901  	testCases := []struct {
   902  		Name           string
   903  		JWS            *jose.JSONWebSignature
   904  		Request        *http.Request
   905  		ExpectedResult *probs.ProblemDetails
   906  		ErrorStatType  string
   907  	}{
   908  		{
   909  			Name:    "No extra headers in JWS",
   910  			JWS:     noHeadersJWS,
   911  			Request: noHeadersRequest,
   912  			ExpectedResult: &probs.ProblemDetails{
   913  				Type:       probs.MalformedProblem,
   914  				Detail:     "JWS header parameter 'url' required",
   915  				HTTPStatus: http.StatusBadRequest,
   916  			},
   917  			ErrorStatType: "JWSNoExtraHeaders",
   918  		},
   919  		{
   920  			Name:    "No URL header in JWS",
   921  			JWS:     noURLHeaderJWS,
   922  			Request: noURLHeaderRequest,
   923  			ExpectedResult: &probs.ProblemDetails{
   924  				Type:       probs.MalformedProblem,
   925  				Detail:     "JWS header parameter 'url' required",
   926  				HTTPStatus: http.StatusBadRequest,
   927  			},
   928  			ErrorStatType: "JWSMissingURL",
   929  		},
   930  		{
   931  			Name:    "Wrong URL header in JWS",
   932  			JWS:     wrongURLHeaderJWS,
   933  			Request: wrongURLHeaderRequest,
   934  			ExpectedResult: &probs.ProblemDetails{
   935  				Type:       probs.MalformedProblem,
   936  				Detail:     "JWS header parameter 'url' incorrect. Expected \"http://localhost/test-path\" got \"foobar\"",
   937  				HTTPStatus: http.StatusBadRequest,
   938  			},
   939  			ErrorStatType: "JWSMismatchedURL",
   940  		},
   941  		{
   942  			Name:           "Correct URL header in JWS",
   943  			JWS:            correctURLHeaderJWS,
   944  			Request:        correctURLHeaderRequest,
   945  			ExpectedResult: nil,
   946  		},
   947  	}
   948  
   949  	for _, tc := range testCases {
   950  		t.Run(tc.Name, func(t *testing.T) {
   951  			tc.Request.Header.Add("Content-Type", expectedJWSContentType)
   952  			wfe.stats.joseErrorCount.Reset()
   953  			prob := wfe.validPOSTURL(tc.Request, tc.JWS.Signatures[0].Header)
   954  			if tc.ExpectedResult == nil && prob != nil {
   955  				t.Fatalf("Expected nil result, got %#v", prob)
   956  			} else {
   957  				test.AssertMarshaledEquals(t, prob, tc.ExpectedResult)
   958  			}
   959  			if tc.ErrorStatType != "" {
   960  				test.AssertMetricWithLabelsEquals(
   961  					t, wfe.stats.joseErrorCount, prometheus.Labels{"type": tc.ErrorStatType}, 1)
   962  			}
   963  		})
   964  	}
   965  }
   966  
   967  func (rs requestSigner) multiSigJWS() (*jose.JSONWebSignature, string) {
   968  	privateKeyA := loadKey(rs.t, []byte(test1KeyPrivatePEM))
   969  	privateKeyB := loadKey(rs.t, []byte(test2KeyPrivatePEM))
   970  
   971  	signerKeyA := jose.SigningKey{
   972  		Key:       privateKeyA,
   973  		Algorithm: sigAlgForKey(rs.t, privateKeyA.Public()),
   974  	}
   975  
   976  	signerKeyB := jose.SigningKey{
   977  		Key:       privateKeyB,
   978  		Algorithm: sigAlgForKey(rs.t, privateKeyB.Public()),
   979  	}
   980  
   981  	opts := &jose.SignerOptions{
   982  		NonceSource: rs.nonceService,
   983  		EmbedJWK:    true,
   984  	}
   985  
   986  	signer, err := jose.NewMultiSigner([]jose.SigningKey{signerKeyA, signerKeyB}, opts)
   987  	test.AssertNotError(rs.t, err, "Failed to make multi signer")
   988  
   989  	jws, err := signer.Sign([]byte(""))
   990  	test.AssertNotError(rs.t, err, "Failed to sign req")
   991  
   992  	body := jws.FullSerialize()
   993  	parsedJWS, err := jose.ParseSigned(body)
   994  	test.AssertNotError(rs.t, err, "Failed to parse generated JWS")
   995  
   996  	return parsedJWS, body
   997  }
   998  
   999  func TestParseJWSRequest(t *testing.T) {
  1000  	wfe, _, signer := setupWFE(t)
  1001  
  1002  	_, tooManySigsJWSBody := signer.multiSigJWS()
  1003  
  1004  	_, _, validJWSBody := signer.embeddedJWK(nil, "http://localhost/test-path", "")
  1005  	validJWSRequest := makePostRequestWithPath("test-path", validJWSBody)
  1006  
  1007  	missingSigsJWSBody := `{"payload":"Zm9x","protected":"eyJhbGciOiJSUzI1NiIsImp3ayI6eyJrdHkiOiJSU0EiLCJuIjoicW5BUkxyVDdYejRnUmNLeUxkeWRtQ3ItZXk5T3VQSW1YNFg0MHRoazNvbjI2RmtNem5SM2ZSanM2NmVMSzdtbVBjQlo2dU9Kc2VVUlU2d0FhWk5tZW1vWXgxZE12cXZXV0l5aVFsZUhTRDdROHZCcmhSNnVJb080akF6SlpSLUNoelp1U0R0N2lITi0zeFVWc3B1NVhHd1hVX01WSlpzaFR3cDRUYUZ4NWVsSElUX09iblR2VE9VM1hoaXNoMDdBYmdaS21Xc1ZiWGg1cy1DcklpY1U0T2V4SlBndW5XWl9ZSkp1ZU9LbVR2bkxsVFY0TXpLUjJvWmxCS1oyN1MwLVNmZFZfUUR4X3lkbGU1b01BeUtWdGxBVjM1Y3lQTUlzWU53Z1VHQkNkWV8yVXppNWVYMGxUYzdNUFJ3ejZxUjFraXAtaTU5VmNHY1VRZ3FIVjZGeXF3IiwiZSI6IkFRQUIifSwia2lkIjoiIiwibm9uY2UiOiJyNHpuenZQQUVwMDlDN1JwZUtYVHhvNkx3SGwxZVBVdmpGeXhOSE1hQnVvIiwidXJsIjoiaHR0cDovL2xvY2FsaG9zdC9hY21lL25ldy1yZWcifQ"}`
  1008  	missingSigsJWSRequest := makePostRequestWithPath("test-path", missingSigsJWSBody)
  1009  
  1010  	unprotectedHeadersJWSBody := `
  1011  {
  1012    "header": {
  1013      "alg": "RS256",
  1014      "kid": "unprotected key id"
  1015    },
  1016    "protected": "eyJub25jZSI6ICJibTl1WTJVIiwgInVybCI6ICJodHRwOi8vbG9jYWxob3N0L3Rlc3QiLCAia2lkIjogInRlc3RrZXkifQ", 
  1017    "payload": "Zm9v",
  1018    "signature": "PKWWclRsiHF4bm-nmpxDez6Y_3Mdtu263YeYklbGYt1EiMOLiKY_dr_EqhUUKAKEWysFLO-hQLXVU7kVkHeYWQFFOA18oFgcZgkSF2Pr3DNZrVj9e2gl0eZ2i2jk6X5GYPt1lIfok_DrL92wrxEKGcrmxqXXGm0JgP6Al2VGapKZK2HaYbCHoGvtzNmzUX9rC21sKewq5CquJRvTmvQp5bmU7Q9KeafGibFr0jl6IA3W5LBGgf6xftuUtEVEbKmKaKtaG7tXsQH1mIVOPUZZoLWz9sWJSFLmV0QSXm3ZHV0DrOhLfcADbOCoQBMeGdseBQZuUO541A3BEKGv2Aikjw"
  1019  }
  1020  `
  1021  
  1022  	wrongSignaturesFieldJWSBody := `
  1023  {
  1024    "protected": "eyJub25jZSI6ICJibTl1WTJVIiwgInVybCI6ICJodHRwOi8vbG9jYWxob3N0L3Rlc3QiLCAia2lkIjogInRlc3RrZXkifQ", 
  1025    "payload": "Zm9v",
  1026    "signatures": ["PKWWclRsiHF4bm-nmpxDez6Y_3Mdtu263YeYklbGYt1EiMOLiKY_dr_EqhUUKAKEWysFLO-hQLXVU7kVkHeYWQFFOA18oFgcZgkSF2Pr3DNZrVj9e2gl0eZ2i2jk6X5GYPt1lIfok_DrL92wrxEKGcrmxqXXGm0JgP6Al2VGapKZK2HaYbCHoGvtzNmzUX9rC21sKewq5CquJRvTmvQp5bmU7Q9KeafGibFr0jl6IA3W5LBGgf6xftuUtEVEbKmKaKtaG7tXsQH1mIVOPUZZoLWz9sWJSFLmV0QSXm3ZHV0DrOhLfcADbOCoQBMeGdseBQZuUO541A3BEKGv2Aikjw"]
  1027  }
  1028  `
  1029  
  1030  	testCases := []struct {
  1031  		Name            string
  1032  		Request         *http.Request
  1033  		ExpectedProblem *probs.ProblemDetails
  1034  		ErrorStatType   string
  1035  	}{
  1036  		{
  1037  			Name: "Invalid POST request",
  1038  			// No Content-Length, something that validPOSTRequest should be flagging
  1039  			Request: &http.Request{
  1040  				Method: "POST",
  1041  				URL:    mustParseURL("/"),
  1042  			},
  1043  			ExpectedProblem: &probs.ProblemDetails{
  1044  				Type:       probs.MalformedProblem,
  1045  				Detail:     "missing Content-Length header",
  1046  				HTTPStatus: http.StatusLengthRequired,
  1047  			},
  1048  		},
  1049  		{
  1050  			Name:    "Invalid JWS in POST body",
  1051  			Request: makePostRequestWithPath("test-path", `{`),
  1052  			ExpectedProblem: &probs.ProblemDetails{
  1053  				Type:       probs.MalformedProblem,
  1054  				Detail:     "Parse error reading JWS",
  1055  				HTTPStatus: http.StatusBadRequest,
  1056  			},
  1057  			ErrorStatType: "JWSUnmarshalFailed",
  1058  		},
  1059  		{
  1060  			Name:    "Too few signatures in JWS",
  1061  			Request: missingSigsJWSRequest,
  1062  			ExpectedProblem: &probs.ProblemDetails{
  1063  				Type:       probs.MalformedProblem,
  1064  				Detail:     "POST JWS not signed",
  1065  				HTTPStatus: http.StatusBadRequest,
  1066  			},
  1067  			ErrorStatType: "JWSEmptySignature",
  1068  		},
  1069  		{
  1070  			Name:    "Too many signatures in JWS",
  1071  			Request: makePostRequestWithPath("test-path", tooManySigsJWSBody),
  1072  			ExpectedProblem: &probs.ProblemDetails{
  1073  				Type:       probs.MalformedProblem,
  1074  				Detail:     "JWS \"signatures\" field not allowed. Only the \"signature\" field should contain a signature",
  1075  				HTTPStatus: http.StatusBadRequest,
  1076  			},
  1077  			ErrorStatType: "JWSMultiSig",
  1078  		},
  1079  		{
  1080  			Name:    "Unprotected JWS headers",
  1081  			Request: makePostRequestWithPath("test-path", unprotectedHeadersJWSBody),
  1082  			ExpectedProblem: &probs.ProblemDetails{
  1083  				Type:       probs.MalformedProblem,
  1084  				Detail:     "JWS \"header\" field not allowed. All headers must be in \"protected\" field",
  1085  				HTTPStatus: http.StatusBadRequest,
  1086  			},
  1087  			ErrorStatType: "JWSUnprotectedHeaders",
  1088  		},
  1089  		{
  1090  			Name:    "Unsupported signatures field in JWS",
  1091  			Request: makePostRequestWithPath("test-path", wrongSignaturesFieldJWSBody),
  1092  			ExpectedProblem: &probs.ProblemDetails{
  1093  				Type:       probs.MalformedProblem,
  1094  				Detail:     "JWS \"signatures\" field not allowed. Only the \"signature\" field should contain a signature",
  1095  				HTTPStatus: http.StatusBadRequest,
  1096  			},
  1097  			ErrorStatType: "JWSMultiSig",
  1098  		},
  1099  		{
  1100  			Name:            "Valid JWS in POST request",
  1101  			Request:         validJWSRequest,
  1102  			ExpectedProblem: nil,
  1103  		},
  1104  		{
  1105  			Name: "POST body too large",
  1106  			Request: makePostRequestWithPath("test-path",
  1107  				fmt.Sprintf(`{"a":"%s"}`, strings.Repeat("a", 50000))),
  1108  			ExpectedProblem: &probs.ProblemDetails{
  1109  				Type:       probs.UnauthorizedProblem,
  1110  				Detail:     "request body too large",
  1111  				HTTPStatus: http.StatusForbidden,
  1112  			},
  1113  		},
  1114  	}
  1115  
  1116  	for _, tc := range testCases {
  1117  		t.Run(tc.Name, func(t *testing.T) {
  1118  			wfe.stats.joseErrorCount.Reset()
  1119  			_, prob := wfe.parseJWSRequest(tc.Request)
  1120  			if tc.ExpectedProblem == nil && prob != nil {
  1121  				t.Fatalf("Expected nil problem, got %#v\n", prob)
  1122  			} else {
  1123  				test.AssertMarshaledEquals(t, prob, tc.ExpectedProblem)
  1124  			}
  1125  			if tc.ErrorStatType != "" {
  1126  				test.AssertMetricWithLabelsEquals(
  1127  					t, wfe.stats.joseErrorCount, prometheus.Labels{"type": tc.ErrorStatType}, 1)
  1128  			}
  1129  		})
  1130  	}
  1131  }
  1132  
  1133  func TestExtractJWK(t *testing.T) {
  1134  	wfe, _, signer := setupWFE(t)
  1135  
  1136  	keyIDJWS, _, _ := signer.byKeyID(1, nil, "", "")
  1137  	goodJWS, goodJWK, _ := signer.embeddedJWK(nil, "", "")
  1138  
  1139  	testCases := []struct {
  1140  		Name            string
  1141  		JWS             *jose.JSONWebSignature
  1142  		ExpectedKey     *jose.JSONWebKey
  1143  		ExpectedProblem *probs.ProblemDetails
  1144  	}{
  1145  		{
  1146  			Name: "JWS with wrong auth type (Key ID vs embedded JWK)",
  1147  			JWS:  keyIDJWS,
  1148  			ExpectedProblem: &probs.ProblemDetails{
  1149  				Type:       probs.MalformedProblem,
  1150  				Detail:     "No embedded JWK in JWS header",
  1151  				HTTPStatus: http.StatusBadRequest,
  1152  			},
  1153  		},
  1154  		{
  1155  			Name:        "Valid JWS with embedded JWK",
  1156  			JWS:         goodJWS,
  1157  			ExpectedKey: goodJWK,
  1158  		},
  1159  	}
  1160  
  1161  	for _, tc := range testCases {
  1162  		t.Run(tc.Name, func(t *testing.T) {
  1163  			jwkHeader, prob := wfe.extractJWK(tc.JWS.Signatures[0].Header)
  1164  			if tc.ExpectedProblem == nil && prob != nil {
  1165  				t.Fatalf("Expected nil problem, got %#v\n", prob)
  1166  			} else if tc.ExpectedProblem == nil {
  1167  				test.AssertMarshaledEquals(t, jwkHeader, tc.ExpectedKey)
  1168  			} else {
  1169  				test.AssertMarshaledEquals(t, prob, tc.ExpectedProblem)
  1170  			}
  1171  		})
  1172  	}
  1173  }
  1174  
  1175  func (rs requestSigner) specifyKeyID(keyID string) (*jose.JSONWebSignature, string) {
  1176  	privateKey := loadKey(rs.t, []byte(test1KeyPrivatePEM))
  1177  
  1178  	if keyID == "" {
  1179  		keyID = "this is an invalid non-numeric key ID"
  1180  	}
  1181  
  1182  	jwk := &jose.JSONWebKey{
  1183  		Key:       privateKey,
  1184  		Algorithm: "RSA",
  1185  		KeyID:     keyID,
  1186  	}
  1187  
  1188  	signerKey := jose.SigningKey{
  1189  		Key:       jwk,
  1190  		Algorithm: jose.RS256,
  1191  	}
  1192  
  1193  	opts := &jose.SignerOptions{
  1194  		NonceSource: rs.nonceService,
  1195  		ExtraHeaders: map[jose.HeaderKey]interface{}{
  1196  			"url": "http://localhost",
  1197  		},
  1198  	}
  1199  
  1200  	signer, err := jose.NewSigner(signerKey, opts)
  1201  	test.AssertNotError(rs.t, err, "Failed to make signer")
  1202  
  1203  	jws, err := signer.Sign([]byte(""))
  1204  	test.AssertNotError(rs.t, err, "Failed to sign req")
  1205  
  1206  	body := jws.FullSerialize()
  1207  	parsedJWS, err := jose.ParseSigned(body)
  1208  	test.AssertNotError(rs.t, err, "Failed to parse generated JWS")
  1209  
  1210  	return parsedJWS, body
  1211  }
  1212  
  1213  func TestLookupJWK(t *testing.T) {
  1214  	wfe, _, signer := setupWFE(t)
  1215  
  1216  	embeddedJWS, _, embeddedJWSBody := signer.embeddedJWK(nil, "", "")
  1217  	invalidKeyIDJWS, invalidKeyIDJWSBody := signer.specifyKeyID("https://acme-99.lettuceencrypt.org/acme/reg/1")
  1218  	// ID 100 is mocked to return a non-missing error from sa.GetRegistration
  1219  	errorIDJWS, _, errorIDJWSBody := signer.byKeyID(100, nil, "", "")
  1220  	// ID 102 is mocked to return an account does not exist error from sa.GetRegistration
  1221  	missingIDJWS, _, missingIDJWSBody := signer.byKeyID(102, nil, "", "")
  1222  	// ID 3 is mocked to return a deactivated account from sa.GetRegistration
  1223  	deactivatedIDJWS, _, deactivatedIDJWSBody := signer.byKeyID(3, nil, "", "")
  1224  
  1225  	wfe.LegacyKeyIDPrefix = "https://acme-v00.lettuceencrypt.org/acme/reg/"
  1226  	legacyKeyIDJWS, legacyKeyIDJWSBody := signer.specifyKeyID(wfe.LegacyKeyIDPrefix + "1")
  1227  
  1228  	nonNumericKeyIDJWS, nonNumericKeyIDJWSBody := signer.specifyKeyID(wfe.LegacyKeyIDPrefix + "abcd")
  1229  
  1230  	validJWS, validKey, validJWSBody := signer.byKeyID(1, nil, "", "")
  1231  	validAccountPB, _ := wfe.sa.GetRegistration(context.Background(), &sapb.RegistrationID{Id: 1})
  1232  	validAccount, _ := bgrpc.PbToRegistration(validAccountPB)
  1233  
  1234  	// good key, log event requester is set
  1235  
  1236  	testCases := []struct {
  1237  		Name            string
  1238  		JWS             *jose.JSONWebSignature
  1239  		Request         *http.Request
  1240  		ExpectedProblem *probs.ProblemDetails
  1241  		ExpectedKey     *jose.JSONWebKey
  1242  		ExpectedAccount *core.Registration
  1243  		ErrorStatType   string
  1244  	}{
  1245  		{
  1246  			Name:    "JWS with wrong auth type (embedded JWK vs Key ID)",
  1247  			JWS:     embeddedJWS,
  1248  			Request: makePostRequestWithPath("test-path", embeddedJWSBody),
  1249  			ExpectedProblem: &probs.ProblemDetails{
  1250  				Type:       probs.MalformedProblem,
  1251  				Detail:     "No Key ID in JWS header",
  1252  				HTTPStatus: http.StatusBadRequest,
  1253  			},
  1254  			ErrorStatType: "JWSAuthTypeWrong",
  1255  		},
  1256  		{
  1257  			Name:    "JWS with invalid key ID URL",
  1258  			JWS:     invalidKeyIDJWS,
  1259  			Request: makePostRequestWithPath("test-path", invalidKeyIDJWSBody),
  1260  			ExpectedProblem: &probs.ProblemDetails{
  1261  				Type:       probs.MalformedProblem,
  1262  				Detail:     "KeyID header contained an invalid account URL: \"https://acme-99.lettuceencrypt.org/acme/reg/1\"",
  1263  				HTTPStatus: http.StatusBadRequest,
  1264  			},
  1265  			ErrorStatType: "JWSInvalidKeyID",
  1266  		},
  1267  		{
  1268  			Name:    "JWS with non-numeric account ID in key ID URL",
  1269  			JWS:     nonNumericKeyIDJWS,
  1270  			Request: makePostRequestWithPath("test-path", nonNumericKeyIDJWSBody),
  1271  			ExpectedProblem: &probs.ProblemDetails{
  1272  				Type:       probs.MalformedProblem,
  1273  				Detail:     "Malformed account ID in KeyID header URL: \"https://acme-v00.lettuceencrypt.org/acme/reg/abcd\"",
  1274  				HTTPStatus: http.StatusBadRequest,
  1275  			},
  1276  			ErrorStatType: "JWSInvalidKeyID",
  1277  		},
  1278  		{
  1279  			Name:    "JWS with account ID that causes GetRegistration error",
  1280  			JWS:     errorIDJWS,
  1281  			Request: makePostRequestWithPath("test-path", errorIDJWSBody),
  1282  			ExpectedProblem: &probs.ProblemDetails{
  1283  				Type:       probs.ServerInternalProblem,
  1284  				Detail:     "Error retrieving account \"http://localhost/acme/acct/100\"",
  1285  				HTTPStatus: http.StatusInternalServerError,
  1286  			},
  1287  			ErrorStatType: "JWSKeyIDLookupFailed",
  1288  		},
  1289  		{
  1290  			Name:    "JWS with account ID that doesn't exist",
  1291  			JWS:     missingIDJWS,
  1292  			Request: makePostRequestWithPath("test-path", missingIDJWSBody),
  1293  			ExpectedProblem: &probs.ProblemDetails{
  1294  				Type:       probs.AccountDoesNotExistProblem,
  1295  				Detail:     "Account \"http://localhost/acme/acct/102\" not found",
  1296  				HTTPStatus: http.StatusBadRequest,
  1297  			},
  1298  			ErrorStatType: "JWSKeyIDNotFound",
  1299  		},
  1300  		{
  1301  			Name:    "JWS with account ID that is deactivated",
  1302  			JWS:     deactivatedIDJWS,
  1303  			Request: makePostRequestWithPath("test-path", deactivatedIDJWSBody),
  1304  			ExpectedProblem: &probs.ProblemDetails{
  1305  				Type:       probs.UnauthorizedProblem,
  1306  				Detail:     "Account is not valid, has status \"deactivated\"",
  1307  				HTTPStatus: http.StatusForbidden,
  1308  			},
  1309  			ErrorStatType: "JWSKeyIDAccountInvalid",
  1310  		},
  1311  		{
  1312  			Name:            "Valid JWS with legacy account ID",
  1313  			JWS:             legacyKeyIDJWS,
  1314  			Request:         makePostRequestWithPath("test-path", legacyKeyIDJWSBody),
  1315  			ExpectedKey:     validKey,
  1316  			ExpectedAccount: &validAccount,
  1317  		},
  1318  		{
  1319  			Name:            "Valid JWS with valid account ID",
  1320  			JWS:             validJWS,
  1321  			Request:         makePostRequestWithPath("test-path", validJWSBody),
  1322  			ExpectedKey:     validKey,
  1323  			ExpectedAccount: &validAccount,
  1324  		},
  1325  	}
  1326  	for _, tc := range testCases {
  1327  		t.Run(tc.Name, func(t *testing.T) {
  1328  			wfe.stats.joseErrorCount.Reset()
  1329  			inputLogEvent := newRequestEvent()
  1330  			jwkHeader, acct, prob := wfe.lookupJWK(tc.JWS.Signatures[0].Header, context.Background(), tc.Request, inputLogEvent)
  1331  			if tc.ExpectedProblem == nil && prob != nil {
  1332  				t.Fatalf("Expected nil problem, got %#v\n", prob)
  1333  			} else if tc.ExpectedProblem == nil {
  1334  				inThumb, _ := tc.ExpectedKey.Thumbprint(crypto.SHA256)
  1335  				outThumb, _ := jwkHeader.Thumbprint(crypto.SHA256)
  1336  				test.AssertDeepEquals(t, inThumb, outThumb)
  1337  				test.AssertMarshaledEquals(t, acct, tc.ExpectedAccount)
  1338  				test.AssertEquals(t, inputLogEvent.Requester, acct.ID)
  1339  			} else {
  1340  				test.AssertMarshaledEquals(t, prob, tc.ExpectedProblem)
  1341  			}
  1342  			if tc.ErrorStatType != "" {
  1343  				test.AssertMetricWithLabelsEquals(
  1344  					t, wfe.stats.joseErrorCount, prometheus.Labels{"type": tc.ErrorStatType}, 1)
  1345  			}
  1346  		})
  1347  	}
  1348  }
  1349  
  1350  func TestValidJWSForKey(t *testing.T) {
  1351  	wfe, _, signer := setupWFE(t)
  1352  
  1353  	payload := `{ "test": "payload" }`
  1354  	testURL := "http://localhost/test"
  1355  	goodJWS, goodJWK, _ := signer.embeddedJWK(nil, testURL, payload)
  1356  
  1357  	// badSigJWSBody is a JWS that has had the payload changed by 1 byte to break the signature
  1358  	badSigJWSBody := `{"payload":"Zm9x","protected":"eyJhbGciOiJSUzI1NiIsImp3ayI6eyJrdHkiOiJSU0EiLCJuIjoicW5BUkxyVDdYejRnUmNLeUxkeWRtQ3ItZXk5T3VQSW1YNFg0MHRoazNvbjI2RmtNem5SM2ZSanM2NmVMSzdtbVBjQlo2dU9Kc2VVUlU2d0FhWk5tZW1vWXgxZE12cXZXV0l5aVFsZUhTRDdROHZCcmhSNnVJb080akF6SlpSLUNoelp1U0R0N2lITi0zeFVWc3B1NVhHd1hVX01WSlpzaFR3cDRUYUZ4NWVsSElUX09iblR2VE9VM1hoaXNoMDdBYmdaS21Xc1ZiWGg1cy1DcklpY1U0T2V4SlBndW5XWl9ZSkp1ZU9LbVR2bkxsVFY0TXpLUjJvWmxCS1oyN1MwLVNmZFZfUUR4X3lkbGU1b01BeUtWdGxBVjM1Y3lQTUlzWU53Z1VHQkNkWV8yVXppNWVYMGxUYzdNUFJ3ejZxUjFraXAtaTU5VmNHY1VRZ3FIVjZGeXF3IiwiZSI6IkFRQUIifSwia2lkIjoiIiwibm9uY2UiOiJyNHpuenZQQUVwMDlDN1JwZUtYVHhvNkx3SGwxZVBVdmpGeXhOSE1hQnVvIiwidXJsIjoiaHR0cDovL2xvY2FsaG9zdC9hY21lL25ldy1yZWcifQ","signature":"jcTdxSygm_cvD7KbXqsxgnoPApCTSkV4jolToSOd2ciRkg5W7Yl0ZKEEKwOc-dYIbQiwGiDzisyPCicwWsOUA1WSqHylKvZ3nxSMc6KtwJCW2DaOqcf0EEjy5VjiZJUrOt2c-r6b07tbn8sfOJKwlF2lsOeGi4s-rtvvkeQpAU-AWauzl9G4bv2nDUeCviAZjHx_PoUC-f9GmZhYrbDzAvXZ859ktM6RmMeD0OqPN7bhAeju2j9Gl0lnryZMtq2m0J2m1ucenQBL1g4ZkP1JiJvzd2cAz5G7Ftl2YeJJyWhqNd3qq0GVOt1P11s8PTGNaSoM0iR9QfUxT9A6jxARtg"}`
  1359  	badJWS, err := jose.ParseSigned(badSigJWSBody)
  1360  	test.AssertNotError(t, err, "error loading badSigJWS body")
  1361  
  1362  	// wrongAlgJWS is a JWS that has an invalid "HS256" algorithm in its header
  1363  	wrongAlgJWS := &jose.JSONWebSignature{
  1364  		Signatures: []jose.Signature{
  1365  			{
  1366  				Header: jose.Header{
  1367  					Algorithm: "HS256",
  1368  				},
  1369  			},
  1370  		},
  1371  	}
  1372  
  1373  	// A JWS and HTTP request with a mismatched HTTP URL to JWS "url" header
  1374  	wrongURLHeaders := map[jose.HeaderKey]interface{}{
  1375  		"url": "foobar",
  1376  	}
  1377  	wrongURLHeaderJWS, _ := signer.signExtraHeaders(wrongURLHeaders)
  1378  
  1379  	// badJSONJWS has a valid signature over a body that is not valid JSON
  1380  	badJSONJWS, _, _ := signer.embeddedJWK(nil, testURL, `{`)
  1381  
  1382  	testCases := []struct {
  1383  		Name            string
  1384  		JWS             bJSONWebSignature
  1385  		JWK             *jose.JSONWebKey
  1386  		Body            string
  1387  		ExpectedProblem *probs.ProblemDetails
  1388  		ErrorStatType   string
  1389  		// TODO(#6610): Remove this.
  1390  		SkipConfig bool
  1391  		// TODO(#6610): Remove this.
  1392  		SkipConfigNext bool
  1393  	}{
  1394  		{
  1395  			Name: "JWS with an invalid algorithm",
  1396  			JWS:  bJSONWebSignature{wrongAlgJWS},
  1397  			JWK:  goodJWK,
  1398  			ExpectedProblem: &probs.ProblemDetails{
  1399  				Type:       probs.BadSignatureAlgorithmProblem,
  1400  				Detail:     "JWS signature header contains unsupported algorithm \"HS256\", expected one of RS256, ES256, ES384 or ES512",
  1401  				HTTPStatus: http.StatusBadRequest,
  1402  			},
  1403  			ErrorStatType: "JWSAlgorithmCheckFailed",
  1404  		},
  1405  		{
  1406  			Name: "JWS with an invalid nonce (test/config)",
  1407  			JWS:  bJSONWebSignature{signer.invalidNonce()},
  1408  			JWK:  goodJWK,
  1409  			ExpectedProblem: &probs.ProblemDetails{
  1410  				Type:       probs.BadNonceProblem,
  1411  				Detail:     "JWS has an invalid anti-replay nonce: \"mlol3ov77I5Ui-cdaY_k8IcjK58FvbG0y_BCRrx5rGQ8rjA\"",
  1412  				HTTPStatus: http.StatusBadRequest,
  1413  			},
  1414  			ErrorStatType:  "JWSInvalidNonce",
  1415  			SkipConfigNext: true,
  1416  		},
  1417  		{
  1418  			Name: "JWS with an invalid nonce (test/config-next)",
  1419  			JWS:  bJSONWebSignature{signer.invalidNonce()},
  1420  			JWK:  goodJWK,
  1421  			ExpectedProblem: &probs.ProblemDetails{
  1422  				Type:       probs.BadNonceProblem,
  1423  				Detail:     "JWS has an invalid anti-replay nonce: \"mlolmlol3ov77I5Ui-cdaY_k8IcjK58FvbG0y_BCRrx5rGQ8rjA\"",
  1424  				HTTPStatus: http.StatusBadRequest,
  1425  			},
  1426  			ErrorStatType: "JWSInvalidNonce",
  1427  			SkipConfig:    true,
  1428  		},
  1429  		{
  1430  			Name: "JWS with broken signature",
  1431  			JWS:  bJSONWebSignature{badJWS},
  1432  			JWK:  badJWS.Signatures[0].Header.JSONWebKey,
  1433  			ExpectedProblem: &probs.ProblemDetails{
  1434  				Type:       probs.MalformedProblem,
  1435  				Detail:     "JWS verification error",
  1436  				HTTPStatus: http.StatusBadRequest,
  1437  			},
  1438  			ErrorStatType: "JWSVerifyFailed",
  1439  		},
  1440  		{
  1441  			Name: "JWS with incorrect URL",
  1442  			JWS:  bJSONWebSignature{wrongURLHeaderJWS},
  1443  			JWK:  wrongURLHeaderJWS.Signatures[0].Header.JSONWebKey,
  1444  			ExpectedProblem: &probs.ProblemDetails{
  1445  				Type:       probs.MalformedProblem,
  1446  				Detail:     "JWS header parameter 'url' incorrect. Expected \"http://localhost/test\" got \"foobar\"",
  1447  				HTTPStatus: http.StatusBadRequest,
  1448  			},
  1449  			ErrorStatType: "JWSMismatchedURL",
  1450  		},
  1451  		{
  1452  			Name: "Valid JWS with invalid JSON in the protected body",
  1453  			JWS:  bJSONWebSignature{badJSONJWS},
  1454  			JWK:  goodJWK,
  1455  			ExpectedProblem: &probs.ProblemDetails{
  1456  				Type:       probs.MalformedProblem,
  1457  				Detail:     "Request payload did not parse as JSON",
  1458  				HTTPStatus: http.StatusBadRequest,
  1459  			},
  1460  			ErrorStatType: "JWSBodyUnmarshalFailed",
  1461  		},
  1462  		{
  1463  			Name: "Good JWS and JWK",
  1464  			JWS:  bJSONWebSignature{goodJWS},
  1465  			JWK:  goodJWK,
  1466  		},
  1467  	}
  1468  
  1469  	for _, tc := range testCases {
  1470  		// TODO(#6610): Remove this.
  1471  		t.Run(tc.Name, func(t *testing.T) {
  1472  			// TODO(#6610): Remove this.
  1473  			if os.Getenv("BOULDER_CONFIG_DIR") == "test/config-next" {
  1474  				if tc.SkipConfigNext {
  1475  					t.Skip("Skipping test in config-next")
  1476  				}
  1477  			} else if tc.SkipConfig {
  1478  				t.Skip("Skipping test in config")
  1479  			}
  1480  			wfe.stats.joseErrorCount.Reset()
  1481  			request := makePostRequestWithPath("test", tc.Body)
  1482  			outPayload, prob := wfe.validJWSForKey(context.Background(), &tc.JWS, tc.JWK, request)
  1483  			if tc.ExpectedProblem == nil && prob != nil {
  1484  				t.Fatalf("Expected nil problem, got %#v\n", prob)
  1485  			} else if tc.ExpectedProblem == nil {
  1486  				test.AssertEquals(t, string(outPayload), payload)
  1487  			} else {
  1488  				test.AssertMarshaledEquals(t, prob, tc.ExpectedProblem)
  1489  			}
  1490  			if tc.ErrorStatType != "" {
  1491  				test.AssertMetricWithLabelsEquals(
  1492  					t, wfe.stats.joseErrorCount, prometheus.Labels{"type": tc.ErrorStatType}, 1)
  1493  			}
  1494  		})
  1495  	}
  1496  }
  1497  
  1498  func TestValidPOSTForAccount(t *testing.T) {
  1499  	wfe, _, signer := setupWFE(t)
  1500  
  1501  	validJWS, _, validJWSBody := signer.byKeyID(1, nil, "http://localhost/test", `{"test":"passed"}`)
  1502  	validAccountPB, _ := wfe.sa.GetRegistration(context.Background(), &sapb.RegistrationID{Id: 1})
  1503  	validAccount, _ := bgrpc.PbToRegistration(validAccountPB)
  1504  
  1505  	// ID 102 is mocked to return missing
  1506  	_, _, missingJWSBody := signer.byKeyID(102, nil, "http://localhost/test", "{}")
  1507  
  1508  	// ID 3 is mocked to return deactivated
  1509  	key3 := loadKey(t, []byte(test3KeyPrivatePEM))
  1510  	_, _, deactivatedJWSBody := signer.byKeyID(3, key3, "http://localhost/test", "{}")
  1511  
  1512  	_, _, embeddedJWSBody := signer.embeddedJWK(nil, "http://localhost/test", `{"test":"passed"}`)
  1513  
  1514  	testCases := []struct {
  1515  		Name            string
  1516  		Request         *http.Request
  1517  		ExpectedProblem *probs.ProblemDetails
  1518  		ExpectedPayload string
  1519  		ExpectedAcct    *core.Registration
  1520  		ExpectedJWS     *jose.JSONWebSignature
  1521  		ErrorStatType   string
  1522  	}{
  1523  		{
  1524  			Name:    "Invalid JWS",
  1525  			Request: makePostRequestWithPath("test", "foo"),
  1526  			ExpectedProblem: &probs.ProblemDetails{
  1527  				Type:       probs.MalformedProblem,
  1528  				Detail:     "Parse error reading JWS",
  1529  				HTTPStatus: http.StatusBadRequest,
  1530  			},
  1531  			ErrorStatType: "JWSUnmarshalFailed",
  1532  		},
  1533  		{
  1534  			Name:    "Embedded Key JWS",
  1535  			Request: makePostRequestWithPath("test", embeddedJWSBody),
  1536  			ExpectedProblem: &probs.ProblemDetails{
  1537  				Type:       probs.MalformedProblem,
  1538  				Detail:     "No Key ID in JWS header",
  1539  				HTTPStatus: http.StatusBadRequest,
  1540  			},
  1541  			ErrorStatType: "JWSAuthTypeWrong",
  1542  		},
  1543  		{
  1544  			Name:    "JWS signed by account that doesn't exist",
  1545  			Request: makePostRequestWithPath("test", missingJWSBody),
  1546  			ExpectedProblem: &probs.ProblemDetails{
  1547  				Type:       probs.AccountDoesNotExistProblem,
  1548  				Detail:     "Account \"http://localhost/acme/acct/102\" not found",
  1549  				HTTPStatus: http.StatusBadRequest,
  1550  			},
  1551  			ErrorStatType: "JWSKeyIDNotFound",
  1552  		},
  1553  		{
  1554  			Name:    "JWS signed by account that's deactivated",
  1555  			Request: makePostRequestWithPath("test", deactivatedJWSBody),
  1556  			ExpectedProblem: &probs.ProblemDetails{
  1557  				Type:       probs.UnauthorizedProblem,
  1558  				Detail:     "Account is not valid, has status \"deactivated\"",
  1559  				HTTPStatus: http.StatusForbidden,
  1560  			},
  1561  			ErrorStatType: "JWSKeyIDAccountInvalid",
  1562  		},
  1563  		{
  1564  			Name:            "Valid JWS for account",
  1565  			Request:         makePostRequestWithPath("test", validJWSBody),
  1566  			ExpectedPayload: `{"test":"passed"}`,
  1567  			ExpectedAcct:    &validAccount,
  1568  			ExpectedJWS:     validJWS,
  1569  		},
  1570  	}
  1571  
  1572  	for _, tc := range testCases {
  1573  		t.Run(tc.Name, func(t *testing.T) {
  1574  			wfe.stats.joseErrorCount.Reset()
  1575  			inputLogEvent := newRequestEvent()
  1576  			outPayload, jws, acct, prob := wfe.validPOSTForAccount(tc.Request, context.Background(), inputLogEvent)
  1577  			if tc.ExpectedProblem == nil && prob != nil {
  1578  				t.Fatalf("Expected nil problem, got %#v\n", prob)
  1579  			} else if tc.ExpectedProblem == nil {
  1580  				test.AssertEquals(t, string(outPayload), tc.ExpectedPayload)
  1581  				test.AssertMarshaledEquals(t, acct, tc.ExpectedAcct)
  1582  				test.AssertMarshaledEquals(t, jws, tc.ExpectedJWS)
  1583  			} else {
  1584  				test.AssertMarshaledEquals(t, prob, tc.ExpectedProblem)
  1585  			}
  1586  			if tc.ErrorStatType != "" {
  1587  				test.AssertMetricWithLabelsEquals(
  1588  					t, wfe.stats.joseErrorCount, prometheus.Labels{"type": tc.ErrorStatType}, 1)
  1589  			}
  1590  		})
  1591  	}
  1592  }
  1593  
  1594  // TestValidPOSTAsGETForAccount tests POST-as-GET processing. Because
  1595  // wfe.validPOSTAsGETForAccount calls `wfe.validPOSTForAccount` to do all
  1596  // processing except the empty body test we do not duplicate the
  1597  // `TestValidPOSTForAccount` testcases here.
  1598  func TestValidPOSTAsGETForAccount(t *testing.T) {
  1599  	wfe, _, signer := setupWFE(t)
  1600  
  1601  	// an invalid POST-as-GET request contains a non-empty payload. In this case
  1602  	// we test with the empty JSON payload ("{}")
  1603  	_, _, invalidPayloadRequest := signer.byKeyID(1, nil, "http://localhost/test", "{}")
  1604  	// a valid POST-as-GET request contains an empty payload.
  1605  	_, _, validRequest := signer.byKeyID(1, nil, "http://localhost/test", "")
  1606  
  1607  	testCases := []struct {
  1608  		Name             string
  1609  		Request          *http.Request
  1610  		ExpectedProblem  *probs.ProblemDetails
  1611  		ExpectedLogEvent web.RequestEvent
  1612  	}{
  1613  		{
  1614  			Name:             "Non-empty JWS payload",
  1615  			Request:          makePostRequestWithPath("test", invalidPayloadRequest),
  1616  			ExpectedProblem:  probs.Malformed("POST-as-GET requests must have an empty payload"),
  1617  			ExpectedLogEvent: web.RequestEvent{},
  1618  		},
  1619  		{
  1620  			Name:    "Valid POST-as-GET",
  1621  			Request: makePostRequestWithPath("test", validRequest),
  1622  			ExpectedLogEvent: web.RequestEvent{
  1623  				Method: "POST-as-GET",
  1624  			},
  1625  		},
  1626  	}
  1627  
  1628  	for _, tc := range testCases {
  1629  		ev := newRequestEvent()
  1630  		_, prob := wfe.validPOSTAsGETForAccount(
  1631  			tc.Request,
  1632  			context.Background(),
  1633  			ev)
  1634  		if tc.ExpectedProblem == nil && prob != nil {
  1635  			t.Fatalf("Expected nil problem, got %#v\n", prob)
  1636  		} else if tc.ExpectedProblem != nil {
  1637  			test.AssertMarshaledEquals(t, prob, tc.ExpectedProblem)
  1638  		}
  1639  		test.AssertMarshaledEquals(t, *ev, tc.ExpectedLogEvent)
  1640  	}
  1641  }
  1642  
  1643  type mockSADifferentStoredKey struct {
  1644  	sapb.StorageAuthorityReadOnlyClient
  1645  }
  1646  
  1647  // mockSADifferentStoredKey has a GetRegistration that will always return an
  1648  // account with the test 2 key, no matter the provided ID
  1649  func (sa mockSADifferentStoredKey) GetRegistration(_ context.Context, _ *sapb.RegistrationID, _ ...grpc.CallOption) (*corepb.Registration, error) {
  1650  	return &corepb.Registration{
  1651  		Key:    []byte(test2KeyPublicJSON),
  1652  		Status: string(core.StatusValid),
  1653  	}, nil
  1654  }
  1655  
  1656  func TestValidPOSTForAccountSwappedKey(t *testing.T) {
  1657  	wfe, fc, signer := setupWFE(t)
  1658  	wfe.sa = &mockSADifferentStoredKey{mocks.NewStorageAuthorityReadOnly(fc)}
  1659  	wfe.accountGetter = wfe.sa
  1660  	event := newRequestEvent()
  1661  
  1662  	payload := `{"resource":"ima-payload"}`
  1663  	// Sign a request using test1key
  1664  	_, _, body := signer.byKeyID(1, nil, "http://localhost:4001/test", payload)
  1665  	request := makePostRequestWithPath("test", body)
  1666  
  1667  	// Ensure that ValidPOSTForAccount produces an error since the
  1668  	// mockSADifferentStoredKey will return a different key than the one we used to
  1669  	// sign the request
  1670  	_, _, _, prob := wfe.validPOSTForAccount(request, ctx, event)
  1671  	test.Assert(t, prob != nil, "No error returned for request signed by wrong key")
  1672  	test.AssertEquals(t, prob.Type, probs.MalformedProblem)
  1673  	test.AssertEquals(t, prob.Detail, "JWS verification error")
  1674  }
  1675  
  1676  func TestValidSelfAuthenticatedPOSTGoodKeyErrors(t *testing.T) {
  1677  	wfe, _, signer := setupWFE(t)
  1678  
  1679  	timeoutErrCheckFunc := func(ctx context.Context, keyHash []byte) (bool, error) {
  1680  		return false, context.DeadlineExceeded
  1681  	}
  1682  
  1683  	kp, err := goodkey.NewKeyPolicy(&goodkey.Config{}, timeoutErrCheckFunc)
  1684  	test.AssertNotError(t, err, "making key policy")
  1685  
  1686  	wfe.keyPolicy = kp
  1687  
  1688  	_, _, validJWSBody := signer.embeddedJWK(nil, "http://localhost/test", `{"test":"passed"}`)
  1689  	request := makePostRequestWithPath("test", validJWSBody)
  1690  
  1691  	_, _, prob := wfe.validSelfAuthenticatedPOST(context.Background(), request)
  1692  	test.AssertEquals(t, prob.Type, probs.ServerInternalProblem)
  1693  
  1694  	badKeyCheckFunc := func(ctx context.Context, keyHash []byte) (bool, error) {
  1695  		return false, fmt.Errorf("oh no: %w", goodkey.ErrBadKey)
  1696  	}
  1697  
  1698  	kp, err = goodkey.NewKeyPolicy(&goodkey.Config{}, badKeyCheckFunc)
  1699  	test.AssertNotError(t, err, "making key policy")
  1700  
  1701  	wfe.keyPolicy = kp
  1702  
  1703  	_, _, validJWSBody = signer.embeddedJWK(nil, "http://localhost/test", `{"test":"passed"}`)
  1704  	request = makePostRequestWithPath("test", validJWSBody)
  1705  
  1706  	_, _, prob = wfe.validSelfAuthenticatedPOST(context.Background(), request)
  1707  	test.AssertEquals(t, prob.Type, probs.BadPublicKeyProblem)
  1708  }
  1709  
  1710  func TestValidSelfAuthenticatedPOST(t *testing.T) {
  1711  	wfe, _, signer := setupWFE(t)
  1712  
  1713  	_, validKey, validJWSBody := signer.embeddedJWK(nil, "http://localhost/test", `{"test":"passed"}`)
  1714  
  1715  	_, _, keyIDJWSBody := signer.byKeyID(1, nil, "http://localhost/test", `{"test":"passed"}`)
  1716  
  1717  	testCases := []struct {
  1718  		Name            string
  1719  		Request         *http.Request
  1720  		ExpectedProblem *probs.ProblemDetails
  1721  		ExpectedPayload string
  1722  		ExpectedJWK     *jose.JSONWebKey
  1723  		ErrorStatType   string
  1724  	}{
  1725  		{
  1726  			Name:    "Invalid JWS",
  1727  			Request: makePostRequestWithPath("test", "foo"),
  1728  			ExpectedProblem: &probs.ProblemDetails{
  1729  				Type:       probs.MalformedProblem,
  1730  				Detail:     "Parse error reading JWS",
  1731  				HTTPStatus: http.StatusBadRequest,
  1732  			},
  1733  			ErrorStatType: "JWSUnmarshalFailed",
  1734  		},
  1735  		{
  1736  			Name:    "JWS with key ID",
  1737  			Request: makePostRequestWithPath("test", keyIDJWSBody),
  1738  			ExpectedProblem: &probs.ProblemDetails{
  1739  				Type:       probs.MalformedProblem,
  1740  				Detail:     "No embedded JWK in JWS header",
  1741  				HTTPStatus: http.StatusBadRequest,
  1742  			},
  1743  			ErrorStatType: "JWSAuthTypeWrong",
  1744  		},
  1745  		{
  1746  			Name:            "Valid JWS",
  1747  			Request:         makePostRequestWithPath("test", validJWSBody),
  1748  			ExpectedPayload: `{"test":"passed"}`,
  1749  			ExpectedJWK:     validKey,
  1750  		},
  1751  	}
  1752  
  1753  	for _, tc := range testCases {
  1754  		t.Run(tc.Name, func(t *testing.T) {
  1755  			wfe.stats.joseErrorCount.Reset()
  1756  			outPayload, jwk, prob := wfe.validSelfAuthenticatedPOST(context.Background(), tc.Request)
  1757  			if tc.ExpectedProblem == nil && prob != nil {
  1758  				t.Fatalf("Expected nil problem, got %#v\n", prob)
  1759  			} else if tc.ExpectedProblem == nil {
  1760  				inThumb, _ := tc.ExpectedJWK.Thumbprint(crypto.SHA256)
  1761  				outThumb, _ := jwk.Thumbprint(crypto.SHA256)
  1762  				test.AssertDeepEquals(t, inThumb, outThumb)
  1763  				test.AssertEquals(t, string(outPayload), tc.ExpectedPayload)
  1764  			} else {
  1765  				test.AssertMarshaledEquals(t, prob, tc.ExpectedProblem)
  1766  			}
  1767  			if tc.ErrorStatType != "" {
  1768  				test.AssertMetricWithLabelsEquals(
  1769  					t, wfe.stats.joseErrorCount, prometheus.Labels{"type": tc.ErrorStatType}, 1)
  1770  			}
  1771  		})
  1772  	}
  1773  }
  1774  
  1775  func TestMatchJWSURLs(t *testing.T) {
  1776  	wfe, _, signer := setupWFE(t)
  1777  
  1778  	noURLJWS, _, _ := signer.embeddedJWK(nil, "", "")
  1779  	urlAJWS, _, _ := signer.embeddedJWK(nil, "example.com", "")
  1780  	urlBJWS, _, _ := signer.embeddedJWK(nil, "example.org", "")
  1781  
  1782  	testCases := []struct {
  1783  		Name            string
  1784  		Outer           *jose.JSONWebSignature
  1785  		Inner           *jose.JSONWebSignature
  1786  		ExpectedProblem *probs.ProblemDetails
  1787  		ErrorStatType   string
  1788  	}{
  1789  		{
  1790  			Name:  "Outer JWS without URL",
  1791  			Outer: noURLJWS,
  1792  			Inner: urlAJWS,
  1793  			ExpectedProblem: &probs.ProblemDetails{
  1794  				Type:       probs.MalformedProblem,
  1795  				Detail:     "Outer JWS header parameter 'url' required",
  1796  				HTTPStatus: http.StatusBadRequest,
  1797  			},
  1798  			ErrorStatType: "KeyRolloverOuterJWSNoURL",
  1799  		},
  1800  		{
  1801  			Name:  "Inner JWS without URL",
  1802  			Outer: urlAJWS,
  1803  			Inner: noURLJWS,
  1804  			ExpectedProblem: &probs.ProblemDetails{
  1805  				Type:       probs.MalformedProblem,
  1806  				Detail:     "Inner JWS header parameter 'url' required",
  1807  				HTTPStatus: http.StatusBadRequest,
  1808  			},
  1809  			ErrorStatType: "KeyRolloverInnerJWSNoURL",
  1810  		},
  1811  		{
  1812  			Name:  "Inner and outer JWS without URL",
  1813  			Outer: noURLJWS,
  1814  			Inner: noURLJWS,
  1815  			ExpectedProblem: &probs.ProblemDetails{
  1816  				Type: probs.MalformedProblem,
  1817  				// The Outer JWS is validated first
  1818  				Detail:     "Outer JWS header parameter 'url' required",
  1819  				HTTPStatus: http.StatusBadRequest,
  1820  			},
  1821  			ErrorStatType: "KeyRolloverOuterJWSNoURL",
  1822  		},
  1823  		{
  1824  			Name:  "Mismatched inner and outer JWS URLs",
  1825  			Outer: urlAJWS,
  1826  			Inner: urlBJWS,
  1827  			ExpectedProblem: &probs.ProblemDetails{
  1828  				Type:       probs.MalformedProblem,
  1829  				Detail:     "Outer JWS 'url' value \"example.com\" does not match inner JWS 'url' value \"example.org\"",
  1830  				HTTPStatus: http.StatusBadRequest,
  1831  			},
  1832  			ErrorStatType: "KeyRolloverMismatchedURLs",
  1833  		},
  1834  		{
  1835  			Name:  "Matching inner and outer JWS URLs",
  1836  			Outer: urlAJWS,
  1837  			Inner: urlAJWS,
  1838  		},
  1839  	}
  1840  
  1841  	for _, tc := range testCases {
  1842  		t.Run(tc.Name, func(t *testing.T) {
  1843  			wfe.stats.joseErrorCount.Reset()
  1844  			prob := wfe.matchJWSURLs(tc.Outer.Signatures[0].Header, tc.Inner.Signatures[0].Header)
  1845  			if prob != nil && tc.ExpectedProblem == nil {
  1846  				t.Errorf("matchJWSURLs failed. Expected no problem, got %#v", prob)
  1847  			} else {
  1848  				test.AssertMarshaledEquals(t, prob, tc.ExpectedProblem)
  1849  			}
  1850  			if tc.ErrorStatType != "" {
  1851  				test.AssertMetricWithLabelsEquals(
  1852  					t, wfe.stats.joseErrorCount, prometheus.Labels{"type": tc.ErrorStatType}, 1)
  1853  			}
  1854  		})
  1855  	}
  1856  }
  1857  

View as plain text