Esempio n. 1
0
func NewRawKeyFromPublicKey(pubkey *ecdsa.PublicKey) *rawkey {
	r := &rawkey{}
	r.Curve = curve{pubkey.Curve}
	r.X = buffer.Buffer(pubkey.X.Bytes())
	r.Y = buffer.Buffer(pubkey.Y.Bytes())
	return r
}
Esempio n. 2
0
// Serialize converts the mssage into a compact JSON format
func (s CompactSerialize) Serialize(m *Message) ([]byte, error) {
	if len(m.Signatures) != 1 {
		return nil, errors.New("wrong number of signatures for compact serialization")
	}

	signature := m.Signatures[0]

	hdr := NewHeader()
	if err := hdr.Copy(signature.ProtectedHeader.Header); err != nil {
		return nil, err
	}
	hdr, err := hdr.Merge(signature.PublicHeader)
	if err != nil {
		return nil, err
	}

	hdrbuf, err := hdr.Base64Encode()
	if err != nil {
		return nil, err
	}

	b64payload, err := m.Payload.Base64Encode()
	if err != nil {
		return nil, err
	}
	b64signature, err := buffer.Buffer(signature.Signature).Base64Encode()
	if err != nil {
		return nil, err
	}
	buf := append(append(append(append(hdrbuf, '.'), b64payload...), '.'), b64signature...)

	return buf, nil
}
Esempio n. 3
0
// NewRsaPublicKey creates a new JWK using the given key
func NewRsaPublicKey(pk *rsa.PublicKey) (*RsaPublicKey, error) {
	k := &RsaPublicKey{
		EssentialHeader: &EssentialHeader{KeyType: "RSA"},
		N:               buffer.Buffer(pk.N.Bytes()),
		E:               buffer.FromUint(uint64(pk.E)),
	}
	return k, nil
}
Esempio n. 4
0
func (h Header) Base64Encode() ([]byte, error) {
	b, err := json.Marshal(h)
	if err != nil {
		return nil, err
	}

	return buffer.Buffer(b).Base64Encode()
}
Esempio n. 5
0
// NewRsaPrivateKey creates a new JWK using the given key
func NewRsaPrivateKey(pk *rsa.PrivateKey) (*RsaPrivateKey, error) {
	if len(pk.Primes) < 2 {
		return nil, errors.New("two primes required for RSA private key")
	}

	pub, err := NewRsaPublicKey(&pk.PublicKey)
	if err != nil {
		return nil, err
	}

	k := &RsaPrivateKey{
		RsaPublicKey: pub,
		D:            buffer.Buffer(pk.D.Bytes()),
		P:            buffer.Buffer(pk.Primes[0].Bytes()),
		Q:            buffer.Buffer(pk.Primes[1].Bytes()),
	}

	return k, nil
}
Esempio n. 6
0
func New(hash crypto.Hash, alg, Z, apu, apv, pubinfo, privinfo []byte) *KDF {
	algbuf := buffer.Buffer(alg).NData()
	apubuf := buffer.Buffer(apu).NData()
	apvbuf := buffer.Buffer(apv).NData()

	concat := make([]byte, len(algbuf)+len(apubuf)+len(apvbuf)+len(pubinfo)+len(privinfo))
	n := copy(concat, algbuf)
	n += copy(concat[n:], apubuf)
	n += copy(concat[n:], apvbuf)
	n += copy(concat[n:], pubinfo)
	n += copy(concat[n:], privinfo)

	return &KDF{
		hash:      hash.New(),
		otherinfo: concat,
		round:     1,
		z:         Z,
	}
}
Esempio n. 7
0
func (e EncodedHeader) MarshalJSON() ([]byte, error) {
	buf, err := json.Marshal(e.Header)
	if err != nil {
		return nil, err
	}

	buf, err = buffer.Buffer(buf).Base64Encode()
	if err != nil {
		return nil, err
	}

	return json.Marshal(string(buf))
}
Esempio n. 8
0
// Base64Encode creates the base64 encoded version of the JSON
// representation of this header
func (e EncodedHeader) Base64Encode() ([]byte, error) {
	buf, err := json.Marshal(e.Header)
	if err != nil {
		return nil, err
	}

	buf, err = buffer.Buffer(buf).Base64Encode()
	if err != nil {
		return nil, err
	}

	return buf, nil
}
Esempio n. 9
0
func NewRawKeyFromPrivateKey(privkey *rsa.PrivateKey) *rawkey {
	r := NewRawKeyFromPublicKey(&privkey.PublicKey)
	r.D = buffer.Buffer(privkey.D.Bytes())
	r.P = buffer.Buffer(privkey.Primes[0].Bytes())
	r.Q = buffer.Buffer(privkey.Primes[1].Bytes())
	r.Dp = buffer.Buffer(privkey.Precomputed.Dp.Bytes())
	r.Dq = buffer.Buffer(privkey.Precomputed.Dq.Bytes())
	r.Qi = buffer.Buffer(privkey.Precomputed.Qinv.Bytes())
	return r
}
Esempio n. 10
0
// parseCompact parses a JWS value serialized via compact serialization.
func parseCompact(buf []byte) (*Message, error) {
	parts := bytes.Split(buf, []byte{'.'})
	if len(parts) != 3 {
		return nil, ErrInvalidCompactPartsCount
	}

	enc := base64.RawURLEncoding

	hdrbuf, err := buffer.FromBase64(parts[0])
	if err != nil {
		return nil, err
	}

	debug.Printf("hdrbuf = %s", hdrbuf.Bytes())
	hdr := &EncodedHeader{Header: NewHeader()}
	if err := json.Unmarshal(hdrbuf.Bytes(), hdr.Header); err != nil {
		return nil, err
	}
	hdr.Source = hdrbuf

	payload, err := buffer.FromBase64(parts[1])
	if err != nil {
		return nil, err
	}

	signature := make([]byte, enc.DecodedLen(len(parts[2])))
	if _, err := enc.Decode(signature, parts[2]); err != nil {
		return nil, err
	}
	signature = bytes.TrimRight(signature, "\x00")

	s := NewSignature()
	s.Signature = signature
	s.ProtectedHeader = hdr
	m := &Message{
		Payload:    buffer.Buffer(payload),
		Signatures: []Signature{*s},
	}
	return m, nil
}
Esempio n. 11
0
func TestMultiSigner(t *testing.T) {
	rsakey, err := rsa.GenerateKey(rand.Reader, 2048)
	if !assert.NoError(t, err, "RSA key generated") {
		return
	}

	dsakey, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
	if !assert.NoError(t, err, "ECDSA key generated") {
		return
	}

	ms := &MultiSign{}

	s1, err := NewRsaSign(jwa.RS256, rsakey)
	if !assert.NoError(t, err, "RSA Signer created") {
		return
	}
	s1.PublicHeaders().Set("kid", "2010-12-29")
	ms.AddSigner(s1)

	s2, err := NewEcdsaSign(jwa.ES256, dsakey)
	if !assert.NoError(t, err, "DSA Signer created") {
		return
	}
	s2.PublicHeaders().Set("kid", "e9bc097a-ce51-4036-9562-d2ade882db0d")
	ms.AddSigner(s2)

	v := strings.Join([]string{`{"iss":"joe",`, ` "exp":1300819380,`, ` "http://example.com/is_root":true}`}, "\r\n")
	m, err := ms.Sign(buffer.Buffer(v))
	if !assert.NoError(t, err, "MultiSign succeeded") {
		return
	}

	jsonbuf, _ := json.MarshalIndent(m, "", "  ")
	t.Logf("%s", jsonbuf)
}
Esempio n. 12
0
// Set sets the value of the given key to the given value. If it's
// one of the known keys, it will be set in EssentialHeader field.
// Otherwise, it is set in PrivateParams field.
func (h *Header) Set(key string, value interface{}) error {
	switch key {
	case "alg":
		var v jwa.KeyEncryptionAlgorithm
		s, ok := value.(string)
		if ok {
			v = jwa.KeyEncryptionAlgorithm(s)
		} else {
			v, ok = value.(jwa.KeyEncryptionAlgorithm)
			if !ok {
				return ErrInvalidHeaderValue
			}
		}
		h.Algorithm = v
	case "apu":
		var v buffer.Buffer
		switch value.(type) {
		case buffer.Buffer:
			v = value.(buffer.Buffer)
		case []byte:
			v = buffer.Buffer(value.([]byte))
		case string:
			v = buffer.Buffer(value.(string))
		default:
			return ErrInvalidHeaderValue
		}
		h.AgreementPartyUInfo = v
	case "apv":
		var v buffer.Buffer
		switch value.(type) {
		case buffer.Buffer:
			v = value.(buffer.Buffer)
		case []byte:
			v = buffer.Buffer(value.([]byte))
		case string:
			v = buffer.Buffer(value.(string))
		default:
			return ErrInvalidHeaderValue
		}
		h.AgreementPartyVInfo = v
	case "enc":
		var v jwa.ContentEncryptionAlgorithm
		s, ok := value.(string)
		if ok {
			v = jwa.ContentEncryptionAlgorithm(s)
		} else {
			v, ok = value.(jwa.ContentEncryptionAlgorithm)
			if !ok {
				return ErrInvalidHeaderValue
			}
		}
		h.ContentEncryption = v
	case "cty":
		v, ok := value.(string)
		if !ok {
			return ErrInvalidHeaderValue
		}
		h.ContentType = v
	case "epk":
		v, ok := value.(*jwk.EcdsaPublicKey)
		if !ok {
			return ErrInvalidHeaderValue
		}
		h.EphemeralPublicKey = v
	case "kid":
		v, ok := value.(string)
		if !ok {
			return ErrInvalidHeaderValue
		}
		h.KeyID = v
	case "typ":
		v, ok := value.(string)
		if !ok {
			return ErrInvalidHeaderValue
		}
		h.Type = v
	case "x5t":
		v, ok := value.(string)
		if !ok {
			return ErrInvalidHeaderValue
		}
		h.X509CertThumbprint = v
	case "x5t#256":
		v, ok := value.(string)
		if !ok {
			return ErrInvalidHeaderValue
		}
		h.X509CertThumbprintS256 = v
	case "x5c":
		v, ok := value.([]string)
		if !ok {
			return ErrInvalidHeaderValue
		}
		h.X509CertChain = v
	case "crit":
		v, ok := value.([]string)
		if !ok {
			return ErrInvalidHeaderValue
		}
		h.Critical = v
	case "jku":
		v, ok := value.(string)
		if !ok {
			return ErrInvalidHeaderValue
		}
		u, err := url.Parse(v)
		if err != nil {
			return ErrInvalidHeaderValue
		}
		h.JwkSetURL = u
	case "x5u":
		v, ok := value.(string)
		if !ok {
			return ErrInvalidHeaderValue
		}
		u, err := url.Parse(v)
		if err != nil {
			return ErrInvalidHeaderValue
		}
		h.X509Url = u
	default:
		h.PrivateParams[key] = value
	}
	return nil
}
Esempio n. 13
0
func NewRawKeyFromPublicKey(pubkey *rsa.PublicKey) *rawkey {
	r := &rawkey{}
	r.N = buffer.Buffer(pubkey.N.Bytes())
	r.E = buffer.FromUint(uint64(pubkey.E))
	return r
}
Esempio n. 14
0
// Sign takes a payload, and creates a JWS signed message.
func (m *MultiSign) Sign(payload []byte) (*Message, error) {
	encoded, err := buffer.Buffer(payload).Base64Encode()
	if err != nil {
		return nil, err
	}

	msg := &Message{
		Payload:    buffer.Buffer(payload),
		Signatures: []Signature{},
	}
	for _, signer := range m.Signers {
		protected, err := NewHeader().Merge(signer.PublicHeaders())
		if err != nil {
			return nil, err
		}
		protected, err = protected.Merge(signer.ProtectedHeaders())
		if err != nil {
			return nil, err
		}
		protected.Algorithm = signer.SignatureAlgorithm()

		protbuf, err := protected.Base64Encode()
		if err != nil {
			return nil, err
		}

		siv := append(append(protbuf, '.'), encoded...)

		sigbuf, err := signer.PayloadSign(siv)
		if err != nil {
			return nil, err
		}

		hdr, err := NewHeader().Merge(signer.PublicHeaders())
		if err != nil {
			return nil, err
		}

		/*
			if hdr.KeyID == "" {
				if protected.KeyID != "" {
					// Use the JWK in the protected field...
					hdr.KeyID = protected.KeyID
				} else if signer.Kid() != "" {
					// Or, get it from the signer
					hdr.KeyID = signer.Kid()
				}
			}
		*/

		sig := Signature{
			PublicHeader:    hdr,
			ProtectedHeader: &EncodedHeader{Header: protected},
			Signature:       buffer.Buffer(sigbuf),
		}

		msg.Signatures = append(msg.Signatures, sig)
	}

	return msg, nil
}
Esempio n. 15
0
// TestEncode_HS256Compact tests that https://tools.ietf.org/html/rfc7515#appendix-A.1 works
func TestEncode_HS256Compact(t *testing.T) {
	const hdr = `{"typ":"JWT",` + "\r\n" + ` "alg":"HS256"}`
	const hmacKey = `AyM1SysPpbyDfgZld3umj1qzKObwVMkoqQ-EstJQLr_T-1qS0gZH75aKtMN3Yj0iPS4hcgUuTwjAzZr1Z9CAow`
	const expected = `eyJ0eXAiOiJKV1QiLA0KICJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJqb2UiLA0KICJleHAiOjEzMDA4MTkzODAsDQogImh0dHA6Ly9leGFtcGxlLmNvbS9pc19yb290Ijp0cnVlfQ.dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXk`

	hmacKeyDecoded := buffer.Buffer{}
	hmacKeyDecoded.Base64Decode([]byte(hmacKey))

	sign, err := NewHmacSign(jwa.HS256, hmacKeyDecoded.Bytes())
	if !assert.NoError(t, err, "HmacSign created successfully") {
		return
	}

	hdrbuf, err := buffer.Buffer(hdr).Base64Encode()
	if !assert.NoError(t, err, "base64 encode successful") {
		return
	}
	payload, err := buffer.Buffer(examplePayload).Base64Encode()
	if !assert.NoError(t, err, "base64 encode successful") {
		return
	}

	signingInput := bytes.Join(
		[][]byte{
			hdrbuf,
			payload,
		},
		[]byte{'.'},
	)
	signature, err := sign.PayloadSign(signingInput)
	if !assert.NoError(t, err, "PayloadSign is successful") {
		return
	}
	sigbuf, err := buffer.Buffer(signature).Base64Encode()
	if !assert.NoError(t, err, "base64 encode successful") {
		return
	}

	encoded := bytes.Join(
		[][]byte{
			signingInput,
			sigbuf,
		},
		[]byte{'.'},
	)
	if !assert.Equal(t, expected, string(encoded), "generated compact serialization should match") {
		return
	}

	msg, err := Parse(encoded)
	if !assert.NoError(t, err, "Parsing compact encoded serialization succeeds") {
		return
	}

	hdrs := msg.Signatures[0].MergedHeaders()
	if !assert.Equal(t, hdrs.Algorithm(), jwa.HS256, "Algorithm in header matches") {
		return
	}

	v, err := NewHmacVerify(jwa.HS256, hmacKeyDecoded.Bytes())
	if !assert.NoError(t, err, "HmacVerify created") {
		return
	}
	if !assert.NoError(t, v.Verify(msg), "Verify succeeds") {
		return
	}
}
Esempio n. 16
0
// TestEncode_RS256Compact tests that https://tools.ietf.org/html/rfc7515#appendix-A.2 works
func TestEncode_RS256Compact(t *testing.T) {
	const hdr = `{"alg":"RS256"}`
	const expected = `eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJqb2UiLA0KICJleHAiOjEzMDA4MTkzODAsDQogImh0dHA6Ly9leGFtcGxlLmNvbS9pc19yb290Ijp0cnVlfQ.cC4hiUPoj9Eetdgtv3hF80EGrhuB__dzERat0XF9g2VtQgr9PJbu3XOiZj5RZmh7AAuHIm4Bh-0Qc_lF5YKt_O8W2Fp5jujGbds9uJdbF9CUAr7t1dnZcAcQjbKBYNX4BAynRFdiuB--f_nZLgrnbyTyWzO75vRK5h6xBArLIARNPvkSjtQBMHlb1L07Qe7K0GarZRmB_eSN9383LcOLn6_dO--xi12jzDwusC-eOkHWEsqtFZESc6BfI7noOPqvhJ1phCnvWh6IeYI2w9QOYEUipUTI8np6LbgGY9Fs98rqVt5AXLIhWkWywlVmtVrBp0igcN_IoypGlUPQGe77Rw`
	const jwksrc = `{
    "kty":"RSA",
    "n":"ofgWCuLjybRlzo0tZWJjNiuSfb4p4fAkd_wWJcyQoTbji9k0l8W26mPddxHmfHQp-Vaw-4qPCJrcS2mJPMEzP1Pt0Bm4d4QlL-yRT-SFd2lZS-pCgNMsD1W_YpRPEwOWvG6b32690r2jZ47soMZo9wGzjb_7OMg0LOL-bSf63kpaSHSXndS5z5rexMdbBYUsLA9e-KXBdQOS-UTo7WTBEMa2R2CapHg665xsmtdVMTBQY4uDZlxvb3qCo5ZwKh9kG4LT6_I5IhlJH7aGhyxXFvUK-DWNmoudF8NAco9_h9iaGNj8q2ethFkMLs91kzk2PAcDTW9gb54h4FRWyuXpoQ",
    "e":"AQAB",
    "d":"Eq5xpGnNCivDflJsRQBXHx1hdR1k6Ulwe2JZD50LpXyWPEAeP88vLNO97IjlA7_GQ5sLKMgvfTeXZx9SE-7YwVol2NXOoAJe46sui395IW_GO-pWJ1O0BkTGoVEn2bKVRUCgu-GjBVaYLU6f3l9kJfFNS3E0QbVdxzubSu3Mkqzjkn439X0M_V51gfpRLI9JYanrC4D4qAdGcopV_0ZHHzQlBjudU2QvXt4ehNYTCBr6XCLQUShb1juUO1ZdiYoFaFQT5Tw8bGUl_x_jTj3ccPDVZFD9pIuhLhBOneufuBiB4cS98l2SR_RQyGWSeWjnczT0QU91p1DhOVRuOopznQ",
    "p":"4BzEEOtIpmVdVEZNCqS7baC4crd0pqnRH_5IB3jw3bcxGn6QLvnEtfdUdiYrqBdss1l58BQ3KhooKeQTa9AB0Hw_Py5PJdTJNPY8cQn7ouZ2KKDcmnPGBY5t7yLc1QlQ5xHdwW1VhvKn-nXqhJTBgIPgtldC-KDV5z-y2XDwGUc",
    "q":"uQPEfgmVtjL0Uyyx88GZFF1fOunH3-7cepKmtH4pxhtCoHqpWmT8YAmZxaewHgHAjLYsp1ZSe7zFYHj7C6ul7TjeLQeZD_YwD66t62wDmpe_HlB-TnBA-njbglfIsRLtXlnDzQkv5dTltRJ11BKBBypeeF6689rjcJIDEz9RWdc",
    "dp":"BwKfV3Akq5_MFZDFZCnW-wzl-CCo83WoZvnLQwCTeDv8uzluRSnm71I3QCLdhrqE2e9YkxvuxdBfpT_PI7Yz-FOKnu1R6HsJeDCjn12Sk3vmAktV2zb34MCdy7cpdTh_YVr7tss2u6vneTwrA86rZtu5Mbr1C1XsmvkxHQAdYo0",
    "dq":"h_96-mK1R_7glhsum81dZxjTnYynPbZpHziZjeeHcXYsXaaMwkOlODsWa7I9xXDoRwbKgB719rrmI2oKr6N3Do9U0ajaHF-NKJnwgjMd2w9cjz3_-kyNlxAr2v4IKhGNpmM5iIgOS1VZnOZ68m6_pbLBSp3nssTdlqvd0tIiTHU",
    "qi":"IYd7DHOhrWvxkwPQsRM2tOgrjbcrfvtQJipd-DlcxyVuuM9sQLdgjVk2oy26F0EmpScGLq2MowX7fhd_QJQ3ydy5cY7YIBi87w93IKLEdfnbJtoOPLUW0ITrJReOgo1cq9SbsxYawBgfp_gh6A5603k2-ZQwVK0JKSHuLFkuQ3U"
  }`

	privkey, err := rsautil.PrivateKeyFromJSON([]byte(jwksrc))
	if !assert.NoError(t, err, "parsing jwk should be successful") {
		return
	}

	sign, err := NewRsaSign(jwa.RS256, privkey)
	if !assert.NoError(t, err, "RsaSign created successfully") {
		return
	}

	hdrbuf, err := buffer.Buffer(hdr).Base64Encode()
	if !assert.NoError(t, err, "base64 encode successful") {
		return
	}
	payload, err := buffer.Buffer(examplePayload).Base64Encode()
	if !assert.NoError(t, err, "base64 encode successful") {
		return
	}

	signingInput := bytes.Join(
		[][]byte{
			hdrbuf,
			payload,
		},
		[]byte{'.'},
	)
	signature, err := sign.PayloadSign(signingInput)
	if !assert.NoError(t, err, "PayloadSign is successful") {
		return
	}
	sigbuf, err := buffer.Buffer(signature).Base64Encode()
	if !assert.NoError(t, err, "base64 encode successful") {
		return
	}

	encoded := bytes.Join(
		[][]byte{
			signingInput,
			sigbuf,
		},
		[]byte{'.'},
	)

	if !assert.Equal(t, expected, string(encoded), "generated compact serialization should match") {
		return
	}

	msg, err := Parse(encoded)
	if !assert.NoError(t, err, "Parsing compact encoded serialization succeeds") {
		return
	}

	hdrs := msg.Signatures[0].MergedHeaders()
	if !assert.Equal(t, hdrs.Algorithm(), jwa.RS256, "Algorithm in header matches") {
		return
	}

	v, err := NewRsaVerify(jwa.RS256, &privkey.PublicKey)
	if !assert.NoError(t, err, "Verify created") {
		return
	}

	if !assert.NoError(t, v.Verify(msg), "Verify succeeds") {
		return
	}
}
Esempio n. 17
0
func NewRawKeyFromPrivateKey(privkey *ecdsa.PrivateKey) *rawkey {
	r := NewRawKeyFromPublicKey(&privkey.PublicKey)
	r.D = buffer.Buffer(privkey.D.Bytes())
	return r
}
Esempio n. 18
0
// TestEncode_ES256Compact tests that https://tools.ietf.org/html/rfc7515#appendix-A.3 works
func TestEncode_ES256Compact(t *testing.T) {
	const hdr = `{"alg":"ES256"}`
	const jwksrc = `{
    "kty":"EC",
    "crv":"P-256",
    "x":"f83OJ3D2xF1Bg8vub9tLe1gHMzV76e8Tus9uPHvRVEU",
    "y":"x_FEzRu9m36HLN_tue659LNpXW6pCyStikYjKIWI5a0",
    "d":"jpsQnnGQmL-YBIffH1136cspYG6-0iY7X1fCE9-E9LI"
  }`

	privkey, err := ecdsautil.PrivateKeyFromJSON([]byte(jwksrc))
	if !assert.NoError(t, err, "parsing jwk should be successful") {
		return
	}

	sign, err := NewEcdsaSign(jwa.ES256, privkey)
	if !assert.NoError(t, err, "RsaSign created successfully") {
		return
	}

	hdrbuf, err := buffer.Buffer(hdr).Base64Encode()
	if !assert.NoError(t, err, "base64 encode successful") {
		return
	}
	payload, err := buffer.Buffer(examplePayload).Base64Encode()
	if !assert.NoError(t, err, "base64 encode successful") {
		return
	}

	signingInput := bytes.Join(
		[][]byte{
			hdrbuf,
			payload,
		},
		[]byte{'.'},
	)
	signature, err := sign.PayloadSign(signingInput)
	if !assert.NoError(t, err, "PayloadSign is successful") {
		return
	}
	sigbuf, err := buffer.Buffer(signature).Base64Encode()
	if !assert.NoError(t, err, "base64 encode successful") {
		return
	}

	encoded := bytes.Join(
		[][]byte{
			signingInput,
			sigbuf,
		},
		[]byte{'.'},
	)

	// The signature contains random factor, so unfortunately we can't match
	// the output against a fixed expected outcome. We'll wave doing an
	// exact match, and just try to verify using the signature

	msg, err := Parse(encoded)
	if !assert.NoError(t, err, "Parsing compact encoded serialization succeeds") {
		return
	}

	hdrs := msg.Signatures[0].MergedHeaders()
	if !assert.Equal(t, hdrs.Algorithm(), jwa.ES256, "Algorithm in header matches") {
		return
	}

	v, err := NewEcdsaVerify(jwa.ES256, &privkey.PublicKey)
	if !assert.NoError(t, err, "EcdsaVerify created") {
		return
	}
	if !assert.NoError(t, v.Verify(msg), "Verify succeeds") {
		return
	}
}