-
Notifications
You must be signed in to change notification settings - Fork 30
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
fix(deps): update module github.com/go-jose/go-jose/v3 to v3.0.3 #2747
base: master
Are you sure you want to change the base?
Conversation
7f65718
to
1938b1c
Compare
1938b1c
to
710292e
Compare
[puLL-Merge] - go-jose/[email protected] Diffdiff --git a/.github/workflows/go.yml b/.github/workflows/go.yml
new file mode 100644
index 0000000..435c84c
--- /dev/null
+++ .github/workflows/go.yml
@@ -0,0 +1,53 @@
+name: Go
+
+on:
+ push:
+ branches: [ v3 ]
+ pull_request:
+ branches: [ v3 ]
+
+jobs:
+ build:
+ strategy:
+ fail-fast: false
+ matrix:
+ GO_VERSION:
+ - "1.16.x"
+ - "1.21.5"
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v3
+ with:
+ persist-credentials: false
+
+ - name: Set up Go ${{ matrix.GO_VERSION }}
+ uses: actions/setup-go@v4
+ with:
+ go-version: "${{ matrix.GO_VERSION }}"
+
+ - name: Build
+ run: go build -v ./...
+
+ - name: Test
+ run: go test -v ./...
+
+ govulncheck:
+ strategy:
+ matrix:
+ GO_VERSION:
+ # go1.16 does not meet the minimum requirements to run govulncheck so
+ # we will skip testing against it.
+ - "1.21.5"
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v3
+ with:
+ persist-credentials: false
+
+ - name: Set up Go ${{ matrix.GO_VERSION }}
+ uses: actions/setup-go@v4
+ with:
+ go-version: "${{ matrix.GO_VERSION }}"
+
+ - name: Run govulncheck
+ run: go run golang.org/x/vuln/cmd/govulncheck@latest ./...
diff --git BUG-BOUNTY.md BUG-BOUNTY.md
deleted file mode 100644
index 3305db0..0000000
--- BUG-BOUNTY.md
+++ /dev/null
@@ -1,10 +0,0 @@
-Serious about security
-======================
-
-Square recognizes the important contributions the security research community
-can make. We therefore encourage reporting security issues with the code
-contained in this repository.
-
-If you believe you have discovered a security vulnerability, please follow the
-guidelines at <https://bugcrowd.com/squareopensource>.
-
diff --git a/CHANGELOG.md b/CHANGELOG.md
new file mode 100644
index 0000000..ce2a54e
--- /dev/null
+++ CHANGELOG.md
@@ -0,0 +1,78 @@
+# v4.0.1
+
+## Fixed
+
+ - An attacker could send a JWE containing compressed data that used large
+ amounts of memory and CPU when decompressed by `Decrypt` or `DecryptMulti`.
+ Those functions now return an error if the decompressed data would exceed
+ 250kB or 10x the compressed size (whichever is larger). Thanks to
+ Enze Wang@Alioth and Jianjun Chen@Zhongguancun Lab (@zer0yu and @chenjj)
+ for reporting.
+
+# v4.0.0
+
+This release makes some breaking changes in order to more thoroughly
+address the vulnerabilities discussed in [Three New Attacks Against JSON Web
+Tokens][1], "Sign/encrypt confusion", "Billion hash attack", and "Polyglot
+token".
+
+## Changed
+
+ - Limit JWT encryption types (exclude password or public key types) (#78)
+ - Enforce minimum length for HMAC keys (#85)
+ - jwt: match any audience in a list, rather than requiring all audiences (#81)
+ - jwt: accept only Compact Serialization (#75)
+ - jws: Add expected algorithms for signatures (#74)
+ - Require specifying expected algorithms for ParseEncrypted,
+ ParseSigned, ParseDetached, jwt.ParseEncrypted, jwt.ParseSigned,
+ jwt.ParseSignedAndEncrypted (#69, #74)
+ - Usually there is a small, known set of appropriate algorithms for a program
+ to use and it's a mistake to allow unexpected algorithms. For instance the
+ "billion hash attack" relies in part on programs accepting the PBES2
+ encryption algorithm and doing the necessary work even if they weren't
+ specifically configured to allow PBES2.
+ - Revert "Strip padding off base64 strings" (#82)
+ - The specs require base64url encoding without padding.
+ - Minimum supported Go version is now 1.21
+
+## Added
+
+ - ParseSignedCompact, ParseSignedJSON, ParseEncryptedCompact, ParseEncryptedJSON.
+ - These allow parsing a specific serialization, as opposed to ParseSigned and
+ ParseEncrypted, which try to automatically detect which serialization was
+ provided. It's common to require a specific serialization for a specific
+ protocol - for instance JWT requires Compact serialization.
+
+[1]: https://i.blackhat.com/BH-US-23/Presentations/US-23-Tervoort-Three-New-Attacks-Against-JSON-Web-Tokens.pdf
+
+# v3.0.3
+
+## Fixed
+
+ - Limit decompression output size to prevent a DoS. Backport from v4.0.1.
+
+# v3.0.2
+
+## Fixed
+
+ - DecryptMulti: handle decompression error (#19)
+
+## Changed
+
+ - jwe/CompactSerialize: improve performance (#67)
+ - Increase the default number of PBKDF2 iterations to 600k (#48)
+ - Return the proper algorithm for ECDSA keys (#45)
+
+## Added
+
+ - Add Thumbprint support for opaque signers (#38)
+
+# v3.0.1
+
+## Fixed
+
+ - Security issue: an attacker specifying a large "p2c" value can cause
+ JSONWebEncryption.Decrypt and JSONWebEncryption.DecryptMulti to consume large
+ amounts of CPU, causing a DoS. Thanks to Matt Schwager (@mschwager) for the
+ disclosure and to Tom Tervoort for originally publishing the category of attack.
+ https://i.blackhat.com/BH-US-23/Presentations/US-23-Tervoort-Three-New-Attacks-Against-JSON-Web-Tokens.pdf
diff --git README.md README.md
index b90c7e5..282cd9e 100644
--- README.md
+++ README.md
@@ -1,10 +1,17 @@
# Go JOSE
-[![godoc](http://img.shields.io/badge/godoc-jose_package-blue.svg?style=flat)](https://godoc.org/gopkg.in/go-jose/go-jose.v2)
-[![godoc](http://img.shields.io/badge/godoc-jwt_package-blue.svg?style=flat)](https://godoc.org/gopkg.in/go-jose/go-jose.v2/jwt)
-[![license](http://img.shields.io/badge/license-apache_2.0-blue.svg?style=flat)](https://raw.githubusercontent.com/go-jose/go-jose/master/LICENSE)
-[![build](https://travis-ci.org/go-jose/go-jose.svg?branch=master)](https://travis-ci.org/go-jose/go-jose)
-[![coverage](https://coveralls.io/repos/github/go-jose/go-jose/badge.svg?branch=master)](https://coveralls.io/r/go-jose/go-jose)
+### Versions
+
+[Version 4](https://github.com/go-jose/go-jose)
+([branch](https://github.com/go-jose/go-jose/),
+[doc](https://pkg.go.dev/github.com/go-jose/go-jose/v4), [releases](https://github.com/go-jose/go-jose/releases)) is the current stable version:
+
+ import "github.com/go-jose/go-jose/v4"
+
+The old [square/go-jose](https://github.com/square/go-jose) repo contains the prior v1 and v2 versions, which
+are deprecated.
+
+### Summary
Package jose aims to provide an implementation of the Javascript Object Signing
and Encryption set of standards. This includes support for JSON Web Encryption,
@@ -21,13 +28,13 @@ US maintained blocked list.
## Overview
The implementation follows the
-[JSON Web Encryption](http://dx.doi.org/10.17487/RFC7516) (RFC 7516),
-[JSON Web Signature](http://dx.doi.org/10.17487/RFC7515) (RFC 7515), and
-[JSON Web Token](http://dx.doi.org/10.17487/RFC7519) (RFC 7519) specifications.
+[JSON Web Encryption](https://dx.doi.org/10.17487/RFC7516) (RFC 7516),
+[JSON Web Signature](https://dx.doi.org/10.17487/RFC7515) (RFC 7515), and
+[JSON Web Token](https://dx.doi.org/10.17487/RFC7519) (RFC 7519) specifications.
Tables of supported algorithms are shown below. The library supports both
the compact and JWS/JWE JSON Serialization formats, and has optional support for
multiple recipients. It also comes with a small command-line utility
-([`jose-util`](https://github.com/go-jose/go-jose/tree/master/jose-util))
+([`jose-util`](https://pkg.go.dev/github.com/go-jose/go-jose/jose-util))
for dealing with JOSE messages in a shell.
**Note**: We use a forked version of the `encoding/json` package from the Go
@@ -36,31 +43,10 @@ of [case-insensitive matching](https://www.ietf.org/mail-archive/web/json/curren
This is to avoid differences in interpretation of messages between go-jose and
libraries in other languages.
-### Versions
-
-[Version 2](https://gopkg.in/go-jose/go-jose.v2)
-([branch](https://github.com/go-jose/go-jose/tree/v2),
-[doc](https://godoc.org/gopkg.in/go-jose/go-jose.v2)) is the current stable version:
-
- import "gopkg.in/go-jose/go-jose.v2"
-
-[Version 3](https://github.com/go-jose/go-jose)
-([branch](https://github.com/go-jose/go-jose/tree/master),
-[doc](https://godoc.org/github.com/go-jose/go-jose)) is the under development/unstable version (not released yet):
-
- import "github.com/go-jose/go-jose/v3"
-
-All new feature development takes place on the `master` branch, which we are
-preparing to release as version 3 soon. Version 2 will continue to receive
-critical bug and security fixes. Note that starting with version 3 we are
-using Go modules for versioning instead of `gopkg.in` as before. Version 3 also will require Go version 1.13 or higher.
-
-Version 1 (on the `v1` branch) is frozen and not supported anymore.
-
### Supported algorithms
See below for a table of supported algorithms. Algorithm identifiers match
-the names in the [JSON Web Algorithms](http://dx.doi.org/10.17487/RFC7518)
+the names in the [JSON Web Algorithms](https://dx.doi.org/10.17487/RFC7518)
standard where possible. The Godoc reference has a list of constants.
Key encryption | Algorithm identifier(s)
@@ -103,20 +89,20 @@ allows attaching a key id.
Algorithm(s) | Corresponding types
:------------------------- | -------------------------------
- RSA | *[rsa.PublicKey](http://golang.org/pkg/crypto/rsa/#PublicKey), *[rsa.PrivateKey](http://golang.org/pkg/crypto/rsa/#PrivateKey)
- ECDH, ECDSA | *[ecdsa.PublicKey](http://golang.org/pkg/crypto/ecdsa/#PublicKey), *[ecdsa.PrivateKey](http://golang.org/pkg/crypto/ecdsa/#PrivateKey)
- EdDSA<sup>1</sup> | [ed25519.PublicKey](https://godoc.org/pkg/crypto/ed25519#PublicKey), [ed25519.PrivateKey](https://godoc.org/pkg/crypto/ed25519#PrivateKey)
+ RSA | *[rsa.PublicKey](https://pkg.go.dev/crypto/rsa/#PublicKey), *[rsa.PrivateKey](https://pkg.go.dev/crypto/rsa/#PrivateKey)
+ ECDH, ECDSA | *[ecdsa.PublicKey](https://pkg.go.dev/crypto/ecdsa/#PublicKey), *[ecdsa.PrivateKey](https://pkg.go.dev/crypto/ecdsa/#PrivateKey)
+ EdDSA<sup>1</sup> | [ed25519.PublicKey](https://pkg.go.dev/crypto/ed25519#PublicKey), [ed25519.PrivateKey](https://pkg.go.dev/crypto/ed25519#PrivateKey)
AES, HMAC | []byte
<sup>1. Only available in version 2 or later of the package</sup>
## Examples
-[![godoc](http://img.shields.io/badge/godoc-jose_package-blue.svg?style=flat)](https://godoc.org/gopkg.in/go-jose/go-jose.v2)
-[![godoc](http://img.shields.io/badge/godoc-jwt_package-blue.svg?style=flat)](https://godoc.org/gopkg.in/go-jose/go-jose.v2/jwt)
+[![godoc](https://pkg.go.dev/badge/github.com/go-jose/go-jose/v3.svg)](https://pkg.go.dev/github.com/go-jose/go-jose/v3)
+[![godoc](https://pkg.go.dev/badge/github.com/go-jose/go-jose/v3/jwt.svg)](https://pkg.go.dev/github.com/go-jose/go-jose/v3/jwt)
Examples can be found in the Godoc
reference for this package. The
-[`jose-util`](https://github.com/go-jose/go-jose/tree/master/jose-util)
+[`jose-util`](https://github.com/go-jose/go-jose/tree/v3/jose-util)
subdirectory also contains a small command-line utility which might be useful
as an example as well.
diff --git a/SECURITY.md b/SECURITY.md
new file mode 100644
index 0000000..2f18a75
--- /dev/null
+++ SECURITY.md
@@ -0,0 +1,13 @@
+# Security Policy
+This document explains how to contact the Let's Encrypt security team to report security vulnerabilities.
+
+## Supported Versions
+| Version | Supported |
+| ------- | ----------|
+| >= v3 | ✓ |
+| v2 | ✗ |
+| v1 | ✗ |
+
+## Reporting a vulnerability
+
+Please see [https://letsencrypt.org/contact/#security](https://letsencrypt.org/contact/#security) for the email address to report a vulnerability. Ensure that the subject line for your report contains the word `vulnerability` and is descriptive. Your email should be acknowledged within 24 hours. If you do not receive a response within 24 hours, please follow-up again with another email.
diff --git asymmetric.go asymmetric.go
index 78abc32..d4d4961 100644
--- asymmetric.go
+++ asymmetric.go
@@ -285,6 +285,9 @@ func (ctx rsaDecrypterSigner) signPayload(payload []byte, alg SignatureAlgorithm
switch alg {
case RS256, RS384, RS512:
+ // TODO(https://github.com/go-jose/go-jose/issues/40): As of go1.20, the
+ // random parameter is legacy and ignored, and it can be nil.
+ // https://cs.opensource.google/go/go/+/refs/tags/go1.20:src/crypto/rsa/pkcs1v15.go;l=263;bpv=0;bpt=1
out, err = rsa.SignPKCS1v15(RandReader, ctx.privateKey, hash, hashed)
case PS256, PS384, PS512:
out, err = rsa.SignPSS(RandReader, ctx.privateKey, hash, hashed, &rsa.PSSOptions{
diff --git crypter.go crypter.go
index 6901137..8870e89 100644
--- crypter.go
+++ crypter.go
@@ -21,7 +21,6 @@ import (
"crypto/rsa"
"errors"
"fmt"
- "reflect"
"github.com/go-jose/go-jose/v3/json"
)
@@ -76,14 +75,24 @@ type recipientKeyInfo struct {
type EncrypterOptions struct {
Compression CompressionAlgorithm
- // Optional map of additional keys to be inserted into the protected header
- // of a JWS object. Some specifications which make use of JWS like to insert
- // additional values here. All values must be JSON-serializable.
+ // Optional map of name/value pairs to be inserted into the protected
+ // header of a JWS object. Some specifications which make use of
+ // JWS require additional values here.
+ //
+ // Values will be serialized by [json.Marshal] and must be valid inputs to
+ // that function.
+ //
+ // [json.Marshal]: https://pkg.go.dev/encoding/json#Marshal
ExtraHeaders map[HeaderKey]interface{}
}
// WithHeader adds an arbitrary value to the ExtraHeaders map, initializing it
-// if necessary. It returns itself and so can be used in a fluent style.
+// if necessary, and returns the updated EncrypterOptions.
+//
+// The v parameter will be serialized by [json.Marshal] and must be a valid
+// input to that function.
+//
+// [json.Marshal]: https://pkg.go.dev/encoding/json#Marshal
func (eo *EncrypterOptions) WithHeader(k HeaderKey, v interface{}) *EncrypterOptions {
if eo.ExtraHeaders == nil {
eo.ExtraHeaders = map[HeaderKey]interface{}{}
@@ -111,7 +120,17 @@ func (eo *EncrypterOptions) WithType(typ ContentType) *EncrypterOptions {
// default of 100000 will be used for the count and a 128-bit random salt will
// be generated.
type Recipient struct {
- Algorithm KeyAlgorithm
+ Algorithm KeyAlgorithm
+ // Key must have one of these types:
+ // - ed25519.PublicKey
+ // - *ecdsa.PublicKey
+ // - *rsa.PublicKey
+ // - *JSONWebKey
+ // - JSONWebKey
+ // - []byte (a symmetric key)
+ // - Any type that satisfies the OpaqueKeyEncrypter interface
+ //
+ // The type of Key must match the value of Algorithm.
Key interface{}
KeyID string
PBES2Count int
@@ -150,16 +169,17 @@ func NewEncrypter(enc ContentEncryption, rcpt Recipient, opts *EncrypterOptions)
switch rcpt.Algorithm {
case DIRECT:
// Direct encryption mode must be treated differently
- if reflect.TypeOf(rawKey) != reflect.TypeOf([]byte{}) {
+ keyBytes, ok := rawKey.([]byte)
+ if !ok {
return nil, ErrUnsupportedKeyType
}
- if encrypter.cipher.keySize() != len(rawKey.([]byte)) {
+ if encrypter.cipher.keySize() != len(keyBytes) {
return nil, ErrInvalidKeySize
}
encrypter.keyGenerator = staticKeyGenerator{
- key: rawKey.([]byte),
+ key: keyBytes,
}
- recipientInfo, _ := newSymmetricRecipient(rcpt.Algorithm, rawKey.([]byte))
+ recipientInfo, _ := newSymmetricRecipient(rcpt.Algorithm, keyBytes)
recipientInfo.keyID = keyID
if rcpt.KeyID != "" {
recipientInfo.keyID = rcpt.KeyID
@@ -168,16 +188,16 @@ func NewEncrypter(enc ContentEncryption, rcpt Recipient, opts *EncrypterOptions)
return encrypter, nil
case ECDH_ES:
// ECDH-ES (w/o key wrapping) is similar to DIRECT mode
- typeOf := reflect.TypeOf(rawKey)
- if typeOf != reflect.TypeOf(&ecdsa.PublicKey{}) {
+ keyDSA, ok := rawKey.(*ecdsa.PublicKey)
+ if !ok {
return nil, ErrUnsupportedKeyType
}
encrypter.keyGenerator = ecKeyGenerator{
size: encrypter.cipher.keySize(),
algID: string(enc),
- publicKey: rawKey.(*ecdsa.PublicKey),
+ publicKey: keyDSA,
}
- recipientInfo, _ := newECDHRecipient(rcpt.Algorithm, rawKey.(*ecdsa.PublicKey))
+ recipientInfo, _ := newECDHRecipient(rcpt.Algorithm, keyDSA)
recipientInfo.keyID = keyID
if rcpt.KeyID != "" {
recipientInfo.keyID = rcpt.KeyID
@@ -270,9 +290,8 @@ func makeJWERecipient(alg KeyAlgorithm, encryptionKey interface{}) (recipientKey
recipient, err := makeJWERecipient(alg, encryptionKey.Key)
recipient.keyID = encryptionKey.KeyID
return recipient, err
- }
- if encrypter, ok := encryptionKey.(OpaqueKeyEncrypter); ok {
- return newOpaqueKeyEncrypter(alg, encrypter)
+ case OpaqueKeyEncrypter:
+ return newOpaqueKeyEncrypter(alg, encryptionKey)
}
return recipientKeyInfo{}, ErrUnsupportedKeyType
}
@@ -300,11 +319,11 @@ func newDecrypter(decryptionKey interface{}) (keyDecrypter, error) {
return newDecrypter(decryptionKey.Key)
case *JSONWebKey:
return newDecrypter(decryptionKey.Key)
+ case OpaqueKeyDecrypter:
+ return &opaqueKeyDecrypter{decrypter: decryptionKey}, nil
+ default:
+ return nil, ErrUnsupportedKeyType
}
- if okd, ok := decryptionKey.(OpaqueKeyDecrypter); ok {
- return &opaqueKeyDecrypter{decrypter: okd}, nil
- }
- return nil, ErrUnsupportedKeyType
}
// Implementation of encrypt method producing a JWE object.
@@ -403,9 +422,27 @@ func (ctx *genericEncrypter) Options() EncrypterOptions {
}
}
-// Decrypt and validate the object and return the plaintext. Note that this
-// function does not support multi-recipient, if you desire multi-recipient
+// Decrypt and validate the object and return the plaintext. This
+// function does not support multi-recipient. If you desire multi-recipient
// decryption use DecryptMulti instead.
+//
+// The decryptionKey argument must contain a private or symmetric key
+// and must have one of these types:
+// - *ecdsa.PrivateKey
+// - *rsa.PrivateKey
+// - *JSONWebKey
+// - JSONWebKey
+// - *JSONWebKeySet
+// - JSONWebKeySet
+// - []byte (a symmetric key)
+// - string (a symmetric key)
+// - Any type that satisfies the OpaqueKeyDecrypter interface.
+//
+// Note that ed25519 is only available for signatures, not encryption, so is
+// not an option here.
+//
+// Automatically decompresses plaintext, but returns an error if the decompressed
+// data would be >250kB or >10x the size of the compressed data, whichever is larger.
func (obj JSONWebEncryption) Decrypt(decryptionKey interface{}) ([]byte, error) {
headers := obj.mergedHeaders(nil)
@@ -462,15 +499,24 @@ func (obj JSONWebEncryption) Decrypt(decryptionKey interface{}) ([]byte, error)
// The "zip" header parameter may only be present in the protected header.
if comp := obj.protected.getCompression(); comp != "" {
plaintext, err = decompress(comp, plaintext)
+ if err != nil {
+ return nil, fmt.Errorf("go-jose/go-jose: failed to decompress plaintext: %v", err)
+ }
}
- return plaintext, err
+ return plaintext, nil
}
// DecryptMulti decrypts and validates the object and returns the plaintexts,
// with support for multiple recipients. It returns the index of the recipient
// for which the decryption was successful, the merged headers for that recipient,
// and the plaintext.
+//
+// The decryptionKey argument must have one of the types allowed for the
+// decryptionKey argument of Decrypt().
+//
+// Automatically decompresses plaintext, but returns an error if the decompressed
+// data would be >250kB or >3x the size of the compressed data, whichever is larger.
func (obj JSONWebEncryption) DecryptMulti(decryptionKey interface{}) (int, Header, []byte, error) {
globalHeaders := obj.mergedHeaders(nil)
@@ -532,7 +578,10 @@ func (obj JSONWebEncryption) DecryptMulti(decryptionKey interface{}) (int, Heade
// The "zip" header parameter may only be present in the protected header.
if comp := obj.protected.getCompression(); comp != "" {
- plaintext, _ = decompress(comp, plaintext)
+ plaintext, err = decompress(comp, plaintext)
+ if err != nil {
+ return -1, Header{}, nil, fmt.Errorf("go-jose/go-jose: failed to decompress plaintext: %v", err)
+ }
}
sanitized, err := headers.sanitized()
diff --git crypter_test.go crypter_test.go
index 3577dbe..9f319e7 100644
--- crypter_test.go
+++ crypter_test.go
@@ -23,11 +23,13 @@ import (
"crypto/elliptic"
"crypto/rand"
"crypto/rsa"
+ "encoding/hex"
"fmt"
"io"
"math/big"
"reflect"
"regexp"
+ "strings"
"testing"
)
@@ -40,6 +42,52 @@ var ecTestKey521, _ = ecdsa.GenerateKey(elliptic.P521(), rand.Reader)
var ed25519PublicKey, ed25519PrivateKey, _ = ed25519.GenerateKey(rand.Reader)
+func TestCompressionError(t *testing.T) {
+ testKey, _ := hex.DecodeString("0c5433f844097b2e910db9b3638ff824")
+
+ // // This code was used to provide the test vector `jweBytes` below,
+ // // by running with a local modification of crypter.go to set "zip" to "DEF"
+ // // in the protected header, but not actually compress the payload.
+ // keyAlg := A128KW
+ // encAlg := A128GCM
+ // rcpt := Recipient{Algorithm: keyAlg, Key: testKey}
+ // enc, err := NewEncrypter(encAlg, rcpt, &EncrypterOptions{Compression: DEFLATE})
+ // if err != nil {
+ // t.Fatal(err)
+ // }
+ // jwe, err := enc.Encrypt([]byte("Lorem ipsum dolor sit amet"))
+ // if err != nil {
+ // t.Fatal(err)
+ // }
+ // t.Logf(jwe.FullSerialize())
+
+ jweBytes := `{
+ "protected":"eyJhbGciOiJBMTI4S1ciLCJlbmMiOiJBMTI4R0NNIiwiemlwIjoiREVGIn0",
+ "encrypted_key":"0ZJRSSVctBhIpWvT9Ke2Z-KvSby-kjmi",
+ "iv":"4nuUbYF0BR0KPz1v",
+ "ciphertext":"luLq8QTsJEXbZdRvEzIiHWEitTZTORZqXIk",
+ "tag":"S1j6wvSGtTUCXhED91lUGQ"
+ }`
+ jwe, err := ParseEncrypted(jweBytes)
+ if err != nil {
+ t.Fatal(err)
+ }
+ _, err = jwe.Decrypt(testKey)
+ if err == nil {
+ t.Fatal("expected error on decompression failure")
+ }
+ if !strings.Contains(err.Error(), "failed to decompress plaintext: flate: corrupt input") {
+ t.Errorf("expected flate error, got %s", err)
+ }
+ _, _, _, err = jwe.DecryptMulti(testKey)
+ if err == nil {
+ t.Fatal("expected error on decompression failure")
+ }
+ if !strings.Contains(err.Error(), "failed to decompress plaintext: flate: corrupt input") {
+ t.Errorf("expected flate error, got %s", err)
+ }
+}
+
func RoundtripJWE(keyAlg KeyAlgorithm, encAlg ContentEncryption, compressionAlg CompressionAlgorithm, serializer func(*JSONWebEncryption) (string, error), corrupter func(*JSONWebEncryption) bool, aad []byte, encryptionKey interface{}, decryptionKey interface{}) error {
var rcpt Recipient
switch keyAlg {
@@ -610,6 +658,45 @@ func TestEncrypterWithPBES2(t *testing.T) {
}
}
+func TestRejectTooHighP2C(t *testing.T) {
+ expected := []byte("Lorem ipsum dolor sit amet")
+ algs := []KeyAlgorithm{
+ PBES2_HS256_A128KW, PBES2_HS384_A192KW, PBES2_HS512_A256KW,
+ }
+
+ // Check with both strings and []byte
+ recipientKeys := []interface{}{"password", []byte("password")}
+ for _, key := range recipientKeys {
+ for _, alg := range algs {
+ enc, err := NewEncrypter(A128GCM, Recipient{Algorithm: alg, PBES2Count: 1000001, Key: &JSONWebKey{
+ KeyID: "test-id",
+ Key: key,
+ }}, nil)
+ if err != nil {
+ t.Error(err)
+ }
+
+ ciphertext, _ := enc.Encrypt(expected)
+
+ serialized1, _ := ciphertext.CompactSerialize()
+ serialized2 := ciphertext.FullSerialize()
+
+ parsed1, _ := ParseEncrypted(serialized1)
+ parsed2, _ := ParseEncrypted(serialized2)
+
+ _, err = parsed1.Decrypt("password")
+ if err == nil {
+ t.Fatal("expected error decrypting expensive PBES2 key, got none")
+ }
+
+ _, err = parsed2.Decrypt([]byte("password"))
+ if err == nil {
+ t.Fatal("expected error decrypting expensive PBES2 key, got none")
+ }
+ }
+ }
+}
+
type testKey struct {
enc, dec interface{}
}
diff --git cryptosigner/cryptosigner.go cryptosigner/cryptosigner.go
index dcdbbd9..ddad5c9 100644
--- cryptosigner/cryptosigner.go
+++ cryptosigner/cryptosigner.go
@@ -23,6 +23,7 @@ import (
"crypto"
"crypto/ecdsa"
"crypto/ed25519"
+ "crypto/elliptic"
"crypto/rand"
"crypto/rsa"
"encoding/asn1"
@@ -51,12 +52,20 @@ func (s *cryptoSigner) Public() *jose.JSONWebKey {
}
func (s *cryptoSigner) Algs() []jose.SignatureAlgorithm {
- switch s.signer.Public().(type) {
+ switch key := s.signer.Public().(type) {
case ed25519.PublicKey:
return []jose.SignatureAlgorithm{jose.EdDSA}
case *ecdsa.PublicKey:
- // This could be more precise
- return []jose.SignatureAlgorithm{jose.ES256, jose.ES384, jose.ES512}
+ switch key.Curve {
+ case elliptic.P256():
+ return []jose.SignatureAlgorithm{jose.ES256}
+ case elliptic.P384():
+ return []jose.SignatureAlgorithm{jose.ES384}
+ case elliptic.P521():
+ return []jose.SignatureAlgorithm{jose.ES512}
+ default:
+ return nil
+ }
case *rsa.PublicKey:
return []jose.SignatureAlgorithm{jose.RS256, jose.RS384, jose.RS512, jose.PS256, jose.PS384, jose.PS512}
default:
diff --git cryptosigner/cryptosigner_test.go cryptosigner/cryptosigner_test.go
index be7c4de..28397f2 100644
--- cryptosigner/cryptosigner_test.go
+++ cryptosigner/cryptosigner_test.go
@@ -24,7 +24,10 @@ import (
"crypto/elliptic"
"crypto/rand"
"crypto/rsa"
+ "errors"
"fmt"
+ "io"
+ "reflect"
"testing"
"github.com/go-jose/go-jose/v3"
@@ -135,3 +138,74 @@ func generateSigningTestKey(sigAlg jose.SignatureAlgorithm) (sig, ver interface{
}
return
}
+
+type fakeSigner struct{}
+
+func (fakeSigner) Public() crypto.PublicKey {
+ return []byte("fake-key")
+}
+
+func (fakeSigner) Sign(rand io.Reader, digest []byte, opts crypto.SignerOpts) ([]byte, error) {
+ return nil, errors.New("not a signer")
+}
+
+func Test_cryptoSigner_Algs(t *testing.T) {
+ _, edKey, err := ed25519.GenerateKey(rand.Reader)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ rsaKey, err := rsa.GenerateKey(rand.Reader, 2048)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ p224, err := ecdsa.GenerateKey(elliptic.P224(), rand.Reader)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ p256, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ p384, err := ecdsa.GenerateKey(elliptic.P384(), rand.Reader)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ p521, err := ecdsa.GenerateKey(elliptic.P521(), rand.Reader)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ type fields struct {
+ signer crypto.Signer
+ }
+
+ tests := []struct {
+ name string
+ fields fields
+ want []jose.SignatureAlgorithm
+ }{
+ {"EdDSA", fields{edKey}, []jose.SignatureAlgorithm{jose.EdDSA}},
+ {"ES256", fields{p256}, []jose.SignatureAlgorithm{jose.ES256}},
+ {"ES384", fields{p384}, []jose.SignatureAlgorithm{jose.ES384}},
+ {"ES512", fields{p521}, []jose.SignatureAlgorithm{jose.ES512}},
+ {"RSA", fields{rsaKey}, []jose.SignatureAlgorithm{jose.RS256, jose.RS384, jose.RS512, jose.PS256, jose.PS384, jose.PS512}},
+ {"fail P-224", fields{p224}, nil},
+ {"fail other", fields{fakeSigner{}}, nil},
+ }
+
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ cs := &cryptoSigner{
+ signer: tt.fields.signer,
+ }
+ if got := cs.Algs(); !reflect.DeepEqual(tt.want, got) {
+ t.Errorf("cryptoSigner.Algs() got = %v, want %v", got, tt.want)
+ }
+ })
+ }
+}
diff --git doc.go doc.go
index 71ec1c4..0ad40ca 100644
--- doc.go
+++ doc.go
@@ -15,13 +15,11 @@
*/
/*
-
Package jose aims to provide an implementation of the Javascript Object Signing
and Encryption set of standards. It implements encryption and signing based on
the JSON Web Encryption and JSON Web Signature standards, with optional JSON Web
Token support available in a sub-package. The library supports both the compact
and JWS/JWE JSON Serialization formats, and has optional support for multiple
recipients.
-
*/
package jose
diff --git encoding.go encoding.go
index 968a424..9f07cfd 100644
--- encoding.go
+++ encoding.go
@@ -21,6 +21,7 @@ import (
"compress/flate"
"encoding/base64"
"encoding/binary"
+ "fmt"
"io"
"math/big"
"strings"
@@ -85,7 +86,7 @@ func decompress(algorithm CompressionAlgorithm, input []byte) ([]byte, error) {
}
}
-// Compress with DEFLATE
+// deflate compresses the input.
func deflate(input []byte) ([]byte, error) {
output := new(bytes.Buffer)
@@ -97,15 +98,27 @@ func deflate(input []byte) ([]byte, error) {
return output.Bytes(), err
}
-// Decompress with DEFLATE
+// inflate decompresses the input.
+//
+// Errors if the decompressed data would be >250kB or >10x the size of the
+// compressed data, whichever is larger.
func inflate(input []byte) ([]byte, error) {
output := new(bytes.Buffer)
reader := flate.NewReader(bytes.NewBuffer(input))
- _, err := io.Copy(output, reader)
- if err != nil {
+ maxCompressedSize := 10 * int64(len(input))
+ if maxCompressedSize < 250000 {
+ maxCompressedSize = 250000
+ }
+
+ limit := maxCompressedSize + 1
+ n, err := io.CopyN(output, reader, limit)
+ if err != nil && err != io.EOF {
return nil, err
}
+ if n == limit {
+ return nil, fmt.Errorf("uncompressed data would be too large (>%d bytes)", maxCompressedSize)
+ }
err = reader.Close()
return output.Bytes(), err
@@ -189,3 +202,36 @@ func base64URLDecode(value string) ([]byte, error) {
value = strings.TrimRight(value, "=")
return base64.RawURLEncoding.DecodeString(value)
}
+
+func base64EncodeLen(sl []byte) int {
+ return base64.RawURLEncoding.EncodedLen(len(sl))
+}
+
+func base64JoinWithDots(inputs ...[]byte) string {
+ if len(inputs) == 0 {
+ return ""
+ }
+
+ // Count of dots.
+ totalCount := len(inputs) - 1
+
+ for _, input := range inputs {
+ totalCount += base64EncodeLen(input)
+ }
+
+ out := make([]byte, totalCount)
+ startEncode := 0
+ for i, input := range inputs {
+ base64.RawURLEncoding.Encode(out[startEncode:], input)
+
+ if i == len(inputs)-1 {
+ continue
+ }
+
+ startEncode += base64EncodeLen(input)
+ out[startEncode] = '.'
+ startEncode++
+ }
+
+ return string(out)
+}
diff --git encoding_test.go encoding_test.go
index fc48685..1061cdd 100644
--- encoding_test.go
+++ encoding_test.go
@@ -18,6 +18,8 @@ package jose
import (
"bytes"
+ "crypto/rand"
+ "io"
"strings"
"testing"
)
@@ -57,6 +59,38 @@ func TestInvalidCompression(t *testing.T) {
}
}
+// TestLargeZip tests that we can decompress a large input, so long as its
+// compression ratio is reasonable.
+func TestLargeZip(t *testing.T) {
+ input := new(bytes.Buffer)
+ _, err := io.CopyN(input, rand.Reader, 251000)
+ if err != nil {
+ t.Fatalf("generating input: %s", err)
+ }
+ compressed, err := compress(DEFLATE, input.Bytes())
+ if err != nil {
+ t.Errorf("compressing: %s", err)
+ }
+ t.Logf("compression ratio: %g", float64(len(input.Bytes()))/float64(len(compressed)))
+ _, err = decompress(DEFLATE, compressed)
+ if err != nil {
+ t.Errorf("decompressing large input with low compression ratio: %s", err)
+ }
+}
+
+func TestZipBomb(t *testing.T) {
+ input := strings.Repeat("a", 251000)
+ compressed, err := compress(DEFLATE, []byte(input))
+ if err != nil {
+ t.Errorf("compressing: %s", err)
+ }
+ t.Logf("compression ratio: %d %g", len(compressed), float64(len(input))/float64(len(compressed)))
+ out, err := decompress(DEFLATE, compressed)
+ if err == nil {
+ t.Errorf("expected error decompressing zip bomb, got none. output size %d", len(out))
+ }
+}
+
func TestByteBufferTrim(t *testing.T) {
buf := newBufferFromInt(1)
if !bytes.Equal(buf.data, []byte{1}) {
diff --git go.mod go.mod
index d19ccbf..b110f41 100644
--- go.mod
+++ go.mod
@@ -3,7 +3,7 @@ module github.com/go-jose/go-jose/v3
go 1.12
require (
- github.com/google/go-cmp v0.5.0
- github.com/stretchr/testify v1.6.1
- golang.org/x/crypto v0.0.0-20190911031432-227b76d455e7
+ github.com/google/go-cmp v0.5.9
+ github.com/stretchr/testify v1.7.0
+ golang.org/x/crypto v0.19.0
)
diff --git go.sum go.sum
index a77b2d6..57b5426 100644
--- go.sum
+++ go.sum
@@ -1,33 +1,54 @@
github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
-github.com/google/go-cmp v0.4.0 h1:xsAVV57WRhGj6kEIi8ReJzQlHHqcBYCElAvkovg3B/4=
-github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
-github.com/google/go-cmp v0.4.1 h1:/exdXoGamhu5ONeUJH0deniYLWYvQwW66yvlfiiKTu0=
-github.com/google/go-cmp v0.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
-github.com/google/go-cmp v0.5.0 h1:/QaMHBdZ26BB3SSst0Iwl10Epc+xhTquomWX0oZEB6w=
-github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
+github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
+github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
-github.com/stretchr/objx v0.1.0 h1:4G4v2dO3VZwixGIRoQ5Lfboy6nUhCyYzaqnIAPPhYs4=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
-github.com/stretchr/testify v1.5.1 h1:nOGnQDM7FYENwehXlg/kFVnos3rEvtKTjRvOWSzb6H4=
-github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
-github.com/stretchr/testify v1.6.0 h1:jlIyCplCJFULU/01vCkhKuTyc3OorI3bJFuw6obfgho=
-github.com/stretchr/testify v1.6.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
-github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0=
-github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
+github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
+github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
+github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
-golang.org/x/crypto v0.0.0-20190911031432-227b76d455e7 h1:0hQKqeLdqlt5iIwVOBErRisrHJAN57yOiPRQItI20fU=
-golang.org/x/crypto v0.0.0-20190911031432-227b76d455e7/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
-golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
+golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
+golang.org/x/crypto v0.6.0 h1:qfktjS5LUO+fFKeJXZ+ikTRijMmljikvG68fpMMruSc=
+golang.org/x/crypto v0.6.0/go.mod h1:OFC/31mSvZgRz0V1QTNCzfAI1aIRzbiufJtkMIlEp58=
+golang.org/x/crypto v0.19.0 h1:ENy+Az/9Y1vSrlrvBSyna3PITt4tiZLf7sgCjZBX7Wo=
+golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU=
+golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
+golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
+golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
+golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
+golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
+golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
+golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
+golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
-golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
+golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
+golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
+golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
+golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo=
+golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
-golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4=
-golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
+golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
+golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
+golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
+golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
+golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
+golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
+golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
+golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
+golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
+golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
-gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw=
-gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
diff --git jose-util/crypto.go jose-util/crypto.go
index 1a100b2..5710dc3 100644
--- jose-util/crypto.go
+++ jose-util/crypto.go
@@ -17,8 +17,8 @@
package main
import (
- jose "github.com/go-jose/go-jose/v3"
"github.com/go-jose/go-jose/jose-util/generator"
+ jose "github.com/go-jose/go-jose/v3"
)
func encrypt() {
diff --git jose-util/generate.go jose-util/generate.go
index eae9fee..1f5b6c4 100644
--- jose-util/generate.go
+++ jose-util/generate.go
@@ -22,8 +22,8 @@ import (
"errors"
"fmt"
- "github.com/go-jose/go-jose/v3"
"github.com/go-jose/go-jose/jose-util/generator"
+ "github.com/go-jose/go-jose/v3"
)
func generate() {
diff --git jose-util/go.mod jose-util/go.mod
index 3428bed..fb46edc 100644
--- jose-util/go.mod
+++ jose-util/go.mod
@@ -5,7 +5,7 @@ go 1.12
require (
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc // indirect
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf // indirect
- github.com/go-jose/go-jose/v3 v3.0.0-00010101000000-000000000000
+ github.com/go-jose/go-jose/v3 v3.0.1
gopkg.in/alecthomas/kingpin.v2 v2.2.6
)
diff --git jose-util/go.sum jose-util/go.sum
index 76dfdd1..daf821b 100644
--- jose-util/go.sum
+++ jose-util/go.sum
@@ -4,21 +4,45 @@ github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf h1:qet1QNfXsQxTZq
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
+github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
-github.com/stretchr/testify v1.5.1 h1:nOGnQDM7FYENwehXlg/kFVnos3rEvtKTjRvOWSzb6H4=
-github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
+github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
+github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
+github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
-golang.org/x/crypto v0.0.0-20190911031432-227b76d455e7 h1:0hQKqeLdqlt5iIwVOBErRisrHJAN57yOiPRQItI20fU=
-golang.org/x/crypto v0.0.0-20190911031432-227b76d455e7/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
-golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
+golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
+golang.org/x/crypto v0.6.0 h1:qfktjS5LUO+fFKeJXZ+ikTRijMmljikvG68fpMMruSc=
+golang.org/x/crypto v0.6.0/go.mod h1:OFC/31mSvZgRz0V1QTNCzfAI1aIRzbiufJtkMIlEp58=
+golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
+golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
+golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
+golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
+golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
+golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
-golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
+golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
+golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
+golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
+golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
+golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
+golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
+golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
+golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
+golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
gopkg.in/alecthomas/kingpin.v2 v2.2.6 h1:jMFz6MfLP0/4fUyZle81rXUoxOBFi19VUFKVDOQfozc=
gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
-gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw=
-gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
+gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
+gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
diff --git jose-util/io.go jose-util/io.go
index f6aff56..51b9598 100644
--- jose-util/io.go
+++ jose-util/io.go
@@ -18,7 +18,6 @@ package main
import (
"io"
- "io/ioutil"
"os"
)
@@ -28,9 +27,9 @@ func readInput(path string) []byte {
var err error
if path != "" {
- bytes, err = ioutil.ReadFile(path)
+ bytes, err = os.ReadFile(path)
} else {
- bytes, err = ioutil.ReadAll(os.Stdin)
+ bytes, err = io.ReadAll(os.Stdin)
}
app.FatalIfError(err, "unable to read input")
@@ -57,7 +56,7 @@ func writeOutput(path string, data []byte) {
var err error
if path != "" {
- err = ioutil.WriteFile(path, data, 0644)
+ err = os.WriteFile(path, data, 0644)
} else {
_, err = os.Stdout.Write(data)
}
@@ -82,7 +81,7 @@ func outputStream(path string) *os.File {
// Byte contents of key file
func keyBytes() []byte {
- keyBytes, err := ioutil.ReadFile(*keyFile)
+ keyBytes, err := os.ReadFile(*keyFile)
app.FatalIfError(err, "unable to read key file")
return keyBytes
}
diff --git jose-util/main.go jose-util/main.go
index 4e6e814..c99aabd 100644
--- jose-util/main.go
+++ jose-util/main.go
@@ -25,8 +25,8 @@ import (
kingpin "gopkg.in/alecthomas/kingpin.v2"
- jose "github.com/go-jose/go-jose/v3"
generator "github.com/go-jose/go-jose/jose-util/generator"
+ jose "github.com/go-jose/go-jose/v3"
)
var (
diff --git json/bench_test.go json/bench_test.go
index ed89d11..3b07cac 100644
--- json/bench_test.go
+++ json/bench_test.go
@@ -13,7 +13,7 @@ package json
import (
"bytes"
"compress/gzip"
- "io/ioutil"
+ "io"
"os"
"strings"
"testing"
@@ -47,7 +47,7 @@ func codeInit() {
if err != nil {
panic(err)
}
- data, err := ioutil.ReadAll(gz)
+ data, err := io.ReadAll(gz)
if err != nil {
panic(err)
}
@@ -82,7 +82,7 @@ func BenchmarkCodeEncoder(b *testing.B) {
codeInit()
b.StartTimer()
}
- enc := NewEncoder(ioutil.Discard)
+ enc := NewEncoder(io.Discard)
for i := 0; i < b.N; i++ {
if err := enc.Encode(&codeStruct); err != nil {
b.Fatal("Encode:", err)
diff --git json/decode.go json/decode.go
index 4dbc414..50634dd 100644
--- json/decode.go
+++ json/decode.go
@@ -75,14 +75,13 @@ import (
//
// The JSON null value unmarshals into an interface, map, pointer, or slice
// by setting that Go value to nil. Because null is often used in JSON to mean
-// ``not present,'' unmarshaling a JSON null into any other Go type has no effect
+// “not present,” unmarshaling a JSON null into any other Go type has no effect
// on the value and produces no error.
//
// When unmarshaling quoted strings, invalid UTF-8 or
// invalid UTF-16 surrogate pairs are not treated as an error.
// Instead, they are replaced by the Unicode replacement
// character U+FFFD.
-//
func Unmarshal(data []byte, v interface{}) error {
// Check for well-formedness.
// Avoids filling out half a data structure
diff --git json/encode.go json/encode.go
index ea0a136..98de68c 100644
--- json/encode.go
+++ json/encode.go
@@ -58,6 +58,7 @@ import (
// becomes a member of the object unless
// - the field's tag is "-", or
// - the field is empty and its tag specifies the "omitempty" option.
+//
// The empty values are false, 0, any
// nil pointer or interface value, and any array, slice, map, or string of
// length zero. The object's default key string is the struct field name
@@ -65,28 +66,28 @@ import (
// the struct field's tag value is the key name, followed by an optional comma
// and options. Examples:
//
-// // Field is ignored by this package.
-// Field int `json:"-"`
+// // Field is ignored by this package.
+// Field int `json:"-"`
//
-// // Field appears in JSON as key "myName".
-// Field int `json:"myName"`
+// // Field appears in JSON as key "myName".
+// Field int `json:"myName"`
//
-// // Field appears in JSON as key "myName" and
-// // the field is omitted from the object if its value is empty,
-// // as defined above.
-// Field int `json:"myName,omitempty"`
+// // Field appears in JSON as key "myName" and
+// // the field is omitted from the object if its value is empty,
+// // as defined above.
+// Field int `json:"myName,omitempty"`
//
-// // Field appears in JSON as key "Field" (the default), but
-// // the field is skipped if empty.
-// // Note the leading comma.
-// Field int `json:",omitempty"`
+// // Field appears in JSON as key "Field" (the default), but
+// // the field is skipped if empty.
+// // Note the leading comma.
+// Field int `json:",omitempty"`
//
// The "string" option signals that a field is stored as JSON inside a
// JSON-encoded string. It applies only to fields of string, floating point,
// integer, or boolean types. This extra level of encoding is sometimes used
// when communicating with JavaScript programs:
//
-// Int64String int64 `json:",string"`
+// Int64String int64 `json:",string"`
//
// The key name will be used if it's a non-empty string consisting of
// only Unicode letters, digits, dollar signs, percent signs, hyphens,
@@ -133,7 +134,6 @@ import (
// JSON cannot represent cyclic data structures and Marshal does not
// handle them. Passing cyclic structures to Marshal will result in
// an infinite recursion.
-//
func Marshal(v interface{}) ([]byte, error) {
e := &encodeState{}
err := e.marshal(v)
diff --git json/stream.go json/stream.go
index 9b2b926..f03b171 100644
--- json/stream.go
+++ json/stream.go
@@ -240,7 +240,6 @@ var _ Unmarshaler = (*RawMessage)(nil)
// Number, for JSON numbers
// string, for JSON string literals
// nil, for JSON null
-//
type Token interface{}
const (
diff --git json/stream_test.go json/stream_test.go
index 5d04c9b..cbe81e9 100644
--- json/stream_test.go
+++ json/stream_test.go
@@ -7,7 +7,6 @@ package json
import (
"bytes"
"io"
- "io/ioutil"
"log"
"net"
"net/http"
@@ -102,7 +101,7 @@ func TestDecoderBuffered(t *testing.T) {
if m.Name != "Gopher" {
t.Errorf("Name = %q; want Gopher", m.Name)
}
- rest, err := ioutil.ReadAll(d.Buffered())
+ rest, err := io.ReadAll(d.Buffered())
if err != nil {
t.Fatal(err)
}
@@ -203,7 +202,7 @@ func BenchmarkEncoderEncode(b *testing.B) {
}
v := &T{"foo", "bar"}
for i := 0; i < b.N; i++ {
- if err := NewEncoder(ioutil.Discard).Encode(v); err != nil {
+ if err := NewEncoder(io.Discard).Encode(v); err != nil {
b.Fatal(err)
}
}
diff --git jwe.go jwe.go
index bce3045..4267ac7 100644
--- jwe.go
+++ jwe.go
@@ -252,13 +252,13 @@ func (obj JSONWebEncryption) CompactSerialize() (string, error) {
serializedProtected := mustSerializeJSON(obj.protected)
- return fmt.Sprintf(
- "%s.%s.%s.%s.%s",
- base64.RawURLEncoding.EncodeToString(serializedProtected),
- base64.RawURLEncoding.EncodeToString(obj.recipients[0].encryptedKey),
- base64.RawURLEncoding.EncodeToString(obj.iv),
- base64.RawURLEncoding.EncodeToString(obj.ciphertext),
- base64.RawURLEncoding.EncodeToString(obj.tag)), nil
+ return base64JoinWithDots(
+ serializedProtected,
+ obj.recipients[0].encryptedKey,
+ obj.iv,
+ obj.ciphertext,
+ obj.tag,
+ ), nil
}
// FullSerialize serializes an object using the full JSON serialization format.
diff --git jwk.go jwk.go
index 78ff5ac..e402195 100644
--- jwk.go
+++ jwk.go
@@ -67,9 +67,21 @@ type rawJSONWebKey struct {
X5tSHA256 string `json:"x5t#S256,omitempty"`
}
-// JSONWebKey represents a public or private key in JWK format.
+// JSONWebKey represents a public or private key in JWK format. It can be
+// marshaled into JSON and unmarshaled from JSON.
type JSONWebKey struct {
- // Cryptographic key, can be a symmetric or asymmetric key.
+ // Key is the Go in-memory representation of this key. It must have one
+ // of these types:
+ // - ed25519.PublicKey
+ // - ed25519.PrivateKey
+ // - *ecdsa.PublicKey
+ // - *ecdsa.PrivateKey
+ // - *rsa.PublicKey
+ // - *rsa.PrivateKey
+ // - []byte (a symmetric key)
+ //
+ // When marshaling this JSONWebKey into JSON, the "kty" header parameter
+ // will be automatically set based on the type of this field.
Key interface{}
// Key identifier, parsed from `kid` header.
KeyID string
@@ -389,6 +401,8 @@ func (k *JSONWebKey) Thumbprint(hash crypto.Hash) ([]byte, error) {
input, err = rsaThumbprintInput(key.N, key.E)
case ed25519.PrivateKey:
input, err = edThumbprintInput(ed25519.PublicKey(key[32:]))
+ case OpaqueSigner:
+ return key.Public().Thumbprint(hash)
default:
return nil, fmt.Errorf("go-jose/go-jose: unknown key type '%s'", reflect.TypeOf(key))
}
diff --git jwk_test.go jwk_test.go
index 649da98..1c79ed0 100644
--- jwk_test.go
+++ jwk_test.go
@@ -867,6 +867,24 @@ func TestEd25519Serialization(t *testing.T) {
[]byte(jwk2.Key.(ed25519.PrivateKey).Public().(ed25519.PublicKey))))
}
+type fakeOpaqueSigner struct {
+ signer crypto.Signer
+}
+
+func (f *fakeOpaqueSigner) Public() *JSONWebKey {
+ return &JSONWebKey{
+ Key: f.signer.Public(),
+ }
+}
+
+func (f *fakeOpaqueSigner) Algs() []SignatureAlgorithm {
+ panic("unexpected call to Algs")
+}
+
+func (f *fakeOpaqueSigner) SignPayload(payload []byte, alg SignatureAlgorithm) ([]byte, error) {
+ panic("unexpected call to SignPayload")
+}
+
func TestThumbprint(t *testing.T) {
for i, key := range cookbookJWKs {
var jwk2 JSONWebKey
@@ -884,6 +902,18 @@ func TestThumbprint(t *testing.T) {
if cookbookJWKThumbprints[i] != tpHex {
t.Error("incorrect thumbprint:", i, cookbookJWKThumbprints[i], tpHex)
}
+
+ if signer, ok := jwk2.Key.(crypto.Signer); ok {
+ jwk2.Key = &fakeOpaqueSigner{signer}
+ otp, err := jwk2.Thumbprint(crypto.SHA256)
+ if err != nil {
+ t.Error("unable to compute thumbprint with an OpaqueSigner:", key, err)
+ }
+ otpHex := hex.EncodeToString(otp)
+ if cookbookJWKThumbprints[i] != otpHex {
+ t.Error("incorrect OpaqueSigner thumbprint:", i, cookbookJWKThumbprints[i], otpHex)
+ }
+ }
}
}
diff --git jws.go jws.go
index 865f16a..e37007d 100644
--- jws.go
+++ jws.go
@@ -314,15 +314,18 @@ func (obj JSONWebSignature) compactSerialize(detached bool) (string, error) {
return "", ErrNotSupported
}
- serializedProtected := base64.RawURLEncoding.EncodeToString(mustSerializeJSON(obj.Signatures[0].protected))
- payload := ""
- signature := base64.RawURLEncoding.EncodeToString(obj.Signatures[0].Signature)
+ serializedProtected := mustSerializeJSON(obj.Signatures[0].protected)
+ var payload []byte
if !detached {
- payload = base64.RawURLEncoding.EncodeToString(obj.payload)
+ payload = obj.payload
}
- return fmt.Sprintf("%s.%s.%s", serializedProtected, payload, signature), nil
+ return base64JoinWithDots(
+ serializedProtected,
+ payload,
+ obj.Signatures[0].Signature,
+ ), nil
}
// CompactSerialize serializes an object using the compact serialization format.
diff --git jwt/claims.go jwt/claims.go
index 286be1d..b2a8dc8 100644
--- jwt/claims.go
+++ jwt/claims.go
@@ -119,7 +119,7 @@ func (s Audience) MarshalJSON() ([]byte, error) {
return json.Marshal([]string(s))
}
-//Contains checks whether a given string is included in the Audience
+// Contains checks whether a given string is included in the Audience
func (s Audience) Contains(v string) bool {
for _, a := range s {
if a == v {
diff --git jwt/doc.go jwt/doc.go
index 4cf97b5..30b886e 100644
--- jwt/doc.go
+++ jwt/doc.go
@@ -15,8 +15,6 @@
*/
/*
-
Package jwt provides an implementation of the JSON Web Token standard.
-
*/
package jwt
diff --git opaque.go opaque.go
index fc3e8d2..68db085 100644
--- opaque.go
+++ opaque.go
@@ -121,7 +121,7 @@ func (oke *opaqueKeyEncrypter) encryptKey(cek []byte, alg KeyAlgorithm) (recipie
return oke.encrypter.encryptKey(cek, alg)
}
-//OpaqueKeyDecrypter is an interface that supports decrypting keys with an opaque key.
+// OpaqueKeyDecrypter is an interface that supports decrypting keys with an opaque key.
type OpaqueKeyDecrypter interface {
DecryptKey(encryptedKey []byte, header Header) ([]byte, error)
}
diff --git shared.go shared.go
index fc2505e..489a04e 100644
--- shared.go
+++ shared.go
@@ -183,8 +183,13 @@ type Header struct {
// Unverified certificate chain parsed from x5c header.
certificates []*x509.Certificate
- // Any headers not recognised above get unmarshalled
- // from JSON in a generic manner and placed in this map.
+ // At parse time, each header parameter with a name other than "kid",
+ // "jwk", "alg", "nonce", or "x5c" will have its value passed to
+ // [json.Unmarshal] to unmarshal it into an interface value.
+ // The resulting value will be stored in this map, with the header
+ // parameter name as the key.
+ //
+ // [json.Unmarshal]: https://pkg.go.dev/encoding/json#Unmarshal
ExtraHeaders map[HeaderKey]interface{}
}
diff --git signing.go signing.go
index 81d55f5..52f3d85 100644
--- signing.go
+++ signing.go
@@ -40,6 +40,15 @@ type Signer interface {
}
// SigningKey represents an algorithm/key used to sign a message.
+//
+// Key must have one of these types:
+// - ed25519.PrivateKey
+// - *ecdsa.PrivateKey
+// - *rsa.PrivateKey
+// - *JSONWebKey
+// - JSONWebKey
+// - []byte (an HMAC key)
+// - Any type that satisfies the OpaqueSigner interface
type SigningKey struct {
Algorithm SignatureAlgorithm
Key interface{}
@@ -52,12 +61,22 @@ type SignerOptions struct {
// Optional map of additional keys to be inserted into the protected header
// of a JWS object. Some specifications which make use of JWS like to insert
- // additional values here. All values must be JSON-serializable.
+ // additional values here.
+ //
+ // Values will be serialized by [json.Marshal] and must be valid inputs to
+ // that function.
+ //
+ // [json.Marshal]: https://pkg.go.dev/encoding/json#Marshal
ExtraHeaders map[HeaderKey]interface{}
}
// WithHeader adds an arbitrary value to the ExtraHeaders map, initializing it
-// if necessary. It returns itself and so can be used in a fluent style.
+// if necessary, and returns the updated SignerOptions.
+//
+// The v argument will be serialized by [json.Marshal] and must be a valid
+// input to that function.
+//
+// [json.Marshal]: https://pkg.go.dev/encoding/json#Marshal
func (so *SignerOptions) WithHeader(k HeaderKey, v interface{}) *SignerOptions {
if so.ExtraHeaders == nil {
so.ExtraHeaders = map[HeaderKey]interface{}{}
@@ -173,11 +192,11 @@ func newVerifier(verificationKey interface{}) (payloadVerifier, error) {
return newVerifier(verificationKey.Key)
case *JSONWebKey:
return newVerifier(verificationKey.Key)
+ case OpaqueVerifier:
+ return &opaqueVerifier{verifier: verificationKey}, nil
+ default:
+ return nil, ErrUnsupportedKeyType
}
- if ov, ok := verificationKey.(OpaqueVerifier); ok {
- return &opaqueVerifier{verifier: ov}, nil
- }
- return nil, ErrUnsupportedKeyType
}
func (ctx *genericSigner) addRecipient(alg SignatureAlgorithm, signingKey interface{}) error {
@@ -204,11 +223,11 @@ func makeJWSRecipient(alg SignatureAlgorithm, signingKey interface{}) (recipient
return newJWKSigner(alg, signingKey)
case *JSONWebKey:
return newJWKSigner(alg, *signingKey)
+ case OpaqueSigner:
+ return newOpaqueSigner(alg, signingKey)
+ default:
+ return recipientSigInfo{}, ErrUnsupportedKeyType
}
- if signer, ok := signingKey.(OpaqueSigner); ok {
- return newOpaqueSigner(alg, signer)
- }
- return recipientSigInfo{}, ErrUnsupportedKeyType
}
func newJWKSigner(alg SignatureAlgorithm, signingKey JSONWebKey) (recipientSigInfo, error) {
@@ -321,12 +340,21 @@ func (ctx *genericSigner) Options() SignerOptions {
}
// Verify validates the signature on the object and returns the payload.
-// This function does not support multi-signature, if you desire multi-sig
+// This function does not support multi-signature. If you desire multi-signature
// verification use VerifyMulti instead.
//
// Be careful when verifying signatures based on embedded JWKs inside the
// payload header. You cannot assume that the key received in a payload is
// trusted.
+//
+// The verificationKey argument must have one of these types:
+// - ed25519.PublicKey
+// - *ecdsa.PublicKey
+// - *rsa.PublicKey
+// - *JSONWebKey
+// - JSONWebKey
+// - []byte (an HMAC key)
+// - Any type that implements the OpaqueVerifier interface.
func (obj JSONWebSignature) Verify(verificationKey interface{}) ([]byte, error) {
err := obj.DetachedVerify(obj.payload, verificationKey)
if err != nil {
@@ -346,6 +374,9 @@ func (obj JSONWebSignature) UnsafePayloadWithoutVerification() []byte {
// most cases, you will probably want to use Verify instead. DetachedVerify
// is only useful if you have a payload and signature that are separated from
// each other.
+//
+// The verificationKey argument must have one of the types allowed for the
+// verificationKey argument of JSONWebSignature.Verify().
func (obj JSONWebSignature) DetachedVerify(payload []byte, verificationKey interface{}) error {
key := tryJWKS(verificationKey, obj.headers()...)
verifier, err := newVerifier(key)
@@ -388,6 +419,9 @@ func (obj JSONWebSignature) DetachedVerify(payload []byte, verificationKey inter
// returns the index of the signature that was verified, along with the signature
// object and the payload. We return the signature and index to guarantee that
// callers are getting the verified value.
+//
+// The verificationKey argument must have one of the types allowed for the
+// verificationKey argument of JSONWebSignature.Verify().
func (obj JSONWebSignature) VerifyMulti(verificationKey interface{}) (int, Signature, []byte, error) {
idx, sig, err := obj.DetachedVerifyMulti(obj.payload, verificationKey)
if err != nil {
@@ -405,6 +439,9 @@ func (obj JSONWebSignature) VerifyMulti(verificationKey interface{}) (int, Signa
// DetachedVerifyMulti is only useful if you have a payload and signature that are
// separated from each other, and the signature can have multiple signers at the
// same time.
+//
+// The verificationKey argument must have one of the types allowed for the
+// verificationKey argument of JSONWebSignature.Verify().
func (obj JSONWebSignature) DetachedVerifyMulti(payload []byte, verificationKey interface{}) (int, Signature, error) {
key := tryJWKS(verificationKey, obj.headers()...)
verifier, err := newVerifier(key)
diff --git signing_test.go signing_test.go
index f5ad283..63404dc 100644
--- signing_test.go
+++ signing_test.go
@@ -24,6 +24,8 @@ import (
"fmt"
"io"
"reflect"
+ "runtime"
+ "strings"
"testing"
"github.com/go-jose/go-jose/v3/json"
@@ -150,14 +152,24 @@ func TestRoundtripsJWSCorruptSignature(t *testing.T) {
}
}
+// TestSignerWithBrokenRand tests that using a broken random reader with PSS
+// signature algorithms returns a valid error.
func TestSignerWithBrokenRand(t *testing.T) {
- sigAlgs := []SignatureAlgorithm{RS256, RS384, RS512, PS256, PS384, PS512}
+ // As of go1.20, the random input parameter used in rsa.SignPKCS1v15 is
+ // legacy and ignored, and it can be nil meaning that there's no broken
+ // random-ness test applicable for signing RS256, RS384, or RS512.
+ sigAlgs := []SignatureAlgorithm{PS256, PS384, PS512}
+
+ // We still need to test that users building/testing go-jose on older
+ // versions of go will return an error if the random reader is broken.
+ if strings.HasPrefix(runtime.Version(), "go1.1") {
+ sigAlgs = append(sigAlgs, RS256, RS384, RS512)
+ }
serializer := func(obj *JSONWebSignature) (string, error) { return obj.CompactSerialize() }
corrupter := func(obj *JSONWebSignature) {}
- // Break rand reader
- readers := []func() io.Reader{
+ brokenRandReaders := []func() io.Reader{
// Totally broken
func() io.Reader { return bytes.NewReader([]byte{}) },
// Not enough bytes
@@ -168,7 +180,7 @@ func TestSignerWithBrokenRand(t *testing.T) {
for _, alg := range sigAlgs {
signingKey, verificationKey := GenerateSigningTestKey(alg)
- for i, getReader := range readers {
+ for i, getReader := range brokenRandReaders {
RandReader = getReader()
err := RoundtripJWS(alg, serializer, corrupter, signingKey, ,verificationKey, "test_nonce")
if err == nil {
diff --git symmetric.go symmetric.go
index fb54775..10d8e19 100644
--- symmetric.go
+++ symmetric.go
@@ -40,12 +40,17 @@ var RandReader = rand.Reader
const (
// RFC7518 recommends a minimum of 1,000 iterations:
- // https://tools.ietf.org/html/rfc7518#section-4.8.1.2
+ // - https://tools.ietf.org/html/rfc7518#section-4.8.1.2
+ //
// NIST recommends a minimum of 10,000:
- // https://pages.nist.gov/800-63-3/sp800-63b.html
- // 1Password uses 100,000:
- // https://support.1password.com/pbkdf2/
- defaultP2C = 100000
+ // - https://pages.nist.gov/800-63-3/sp800-63b.html
+ //
+ // 1Password increased in 2023 from 100,000 to 650,000:
+ // - https://support.1password.com/pbkdf2/
+ //
+ // OWASP recommended 600,000 in Dec 2022:
+ // - https://cheatsheetseries.owasp.org/cheatsheets/Password_Storage_Cheat_Sheet.html#pbkdf2
+ defaultP2C = 600000
// Default salt size: 128 bits
defaultP2SSize = 16
)
@@ -415,6 +420,11 @@ func (ctx *symmetricKeyCipher) decryptKey(headers rawHeader, recipient *recipien
if p2c <= 0 {
return nil, fmt.Errorf("go-jose/go-jose: invalid P2C: must be a positive integer")
}
+ if p2c > 1000000 {
+ // An unauthenticated attacker can set a high P2C value. Set an upper limit to avoid
+ // DoS attacks.
+ return nil, fmt.Errorf("go-jose/go-jose: invalid P2C: too high")
+ }
// salt is UTF8(Alg) || 0x00 || Salt Input
alg := headers.getAlgorithm()
DescriptionThis PR updates the go-jose library with several significant changes, including security improvements, better documentation, and infrastructure updates. The changes address vulnerabilities discussed in "Three New Attacks Against JSON Web Tokens" and make various improvements to the codebase. Security Hotspots
ChangesChangesNew Files:
Modified Files:
sequenceDiagram
participant Client
participant JOSE
participant Crypto
participant Compress
Client->>JOSE: Create JWT/JWE
JOSE->>Crypto: Generate/Validate Keys
JOSE->>Compress: Compress Data
Compress-->>JOSE: Check Size Limits
JOSE->>Crypto: Encrypt/Sign
JOSE-->>Client: Return Token
|
This PR contains the following updates:
v3.0.1
->v3.0.3
v3.0.0
->v3.0.3
Release Notes
go-jose/go-jose (github.com/go-jose/go-jose/v3)
v3.0.3
: Version 3.0.3Compare Source
Fixed
v3.0.2
Compare Source
Fixed
Changed
Added
Configuration
📅 Schedule: Branch creation - "* 0-4 * * 3" (UTC), Automerge - At any time (no schedule defined).
🚦 Automerge: Disabled by config. Please merge this manually once you are satisfied.
♻ Rebasing: Whenever PR is behind base branch, or you tick the rebase/retry checkbox.
🔕 Ignore: Close this PR and you won't be reminded about these updates again.
This PR was generated by Mend Renovate. View the repository job log.