deps: update square/go-jose to v2.4.0 (#4518)
This branch also updates the WFE2 parseJWS function to match the error string fixed in the upstream project for the case where a JWS EC public key fails to unmarshal due to an incorrect length. Resolves #4300
This commit is contained in:
parent
a36a90519b
commit
e448e81dc4
2
go.mod
2
go.mod
|
@ -46,6 +46,6 @@ require (
|
||||||
google.golang.org/genproto v0.0.0-20190415143225-d1146b9035b9 // indirect
|
google.golang.org/genproto v0.0.0-20190415143225-d1146b9035b9 // indirect
|
||||||
google.golang.org/grpc v1.20.0
|
google.golang.org/grpc v1.20.0
|
||||||
gopkg.in/go-gorp/gorp.v2 v2.0.0-20180410155428-6032c66e0f5f
|
gopkg.in/go-gorp/gorp.v2 v2.0.0-20180410155428-6032c66e0f5f
|
||||||
gopkg.in/square/go-jose.v2 v2.3.1
|
gopkg.in/square/go-jose.v2 v2.4.0
|
||||||
gopkg.in/yaml.v2 v2.2.2
|
gopkg.in/yaml.v2 v2.2.2
|
||||||
)
|
)
|
||||||
|
|
2
go.sum
2
go.sum
|
@ -165,6 +165,8 @@ gopkg.in/go-gorp/gorp.v2 v2.0.0-20180410155428-6032c66e0f5f h1:zundYZrPliLrlQV98
|
||||||
gopkg.in/go-gorp/gorp.v2 v2.0.0-20180410155428-6032c66e0f5f/go.mod h1:eJwu1bWCXesk9aw26U78PFtctx3Y8haXGmL7x3VJlrw=
|
gopkg.in/go-gorp/gorp.v2 v2.0.0-20180410155428-6032c66e0f5f/go.mod h1:eJwu1bWCXesk9aw26U78PFtctx3Y8haXGmL7x3VJlrw=
|
||||||
gopkg.in/square/go-jose.v2 v2.3.1 h1:SK5KegNXmKmqE342YYN2qPHEnUYeoMiXXl1poUlI+o4=
|
gopkg.in/square/go-jose.v2 v2.3.1 h1:SK5KegNXmKmqE342YYN2qPHEnUYeoMiXXl1poUlI+o4=
|
||||||
gopkg.in/square/go-jose.v2 v2.3.1/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI=
|
gopkg.in/square/go-jose.v2 v2.3.1/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI=
|
||||||
|
gopkg.in/square/go-jose.v2 v2.4.0 h1:0kXPskUMGAXXWJlP05ktEMOV0vmzFQUWw6d+aZJQU8A=
|
||||||
|
gopkg.in/square/go-jose.v2 v2.4.0/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI=
|
||||||
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=
|
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=
|
||||||
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
|
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
|
||||||
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||||
|
|
|
@ -8,11 +8,9 @@ matrix:
|
||||||
- go: tip
|
- go: tip
|
||||||
|
|
||||||
go:
|
go:
|
||||||
- '1.7.x'
|
|
||||||
- '1.8.x'
|
|
||||||
- '1.9.x'
|
|
||||||
- '1.10.x'
|
|
||||||
- '1.11.x'
|
- '1.11.x'
|
||||||
|
- '1.12.x'
|
||||||
|
- tip
|
||||||
|
|
||||||
go_import_path: gopkg.in/square/go-jose.v2
|
go_import_path: gopkg.in/square/go-jose.v2
|
||||||
|
|
||||||
|
|
|
@ -29,7 +29,7 @@ import (
|
||||||
"math/big"
|
"math/big"
|
||||||
|
|
||||||
"golang.org/x/crypto/ed25519"
|
"golang.org/x/crypto/ed25519"
|
||||||
"gopkg.in/square/go-jose.v2/cipher"
|
josecipher "gopkg.in/square/go-jose.v2/cipher"
|
||||||
"gopkg.in/square/go-jose.v2/json"
|
"gopkg.in/square/go-jose.v2/json"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -288,7 +288,7 @@ func (ctx rsaDecrypterSigner) signPayload(payload []byte, alg SignatureAlgorithm
|
||||||
out, err = rsa.SignPKCS1v15(RandReader, ctx.privateKey, hash, hashed)
|
out, err = rsa.SignPKCS1v15(RandReader, ctx.privateKey, hash, hashed)
|
||||||
case PS256, PS384, PS512:
|
case PS256, PS384, PS512:
|
||||||
out, err = rsa.SignPSS(RandReader, ctx.privateKey, hash, hashed, &rsa.PSSOptions{
|
out, err = rsa.SignPSS(RandReader, ctx.privateKey, hash, hashed, &rsa.PSSOptions{
|
||||||
SaltLength: rsa.PSSSaltLengthAuto,
|
SaltLength: rsa.PSSSaltLengthEqualsHash,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -17,8 +17,10 @@
|
||||||
package josecipher
|
package josecipher
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
"crypto"
|
"crypto"
|
||||||
"crypto/ecdsa"
|
"crypto/ecdsa"
|
||||||
|
"crypto/elliptic"
|
||||||
"encoding/binary"
|
"encoding/binary"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -44,16 +46,38 @@ func DeriveECDHES(alg string, apuData, apvData []byte, priv *ecdsa.PrivateKey, p
|
||||||
panic("public key not on same curve as private key")
|
panic("public key not on same curve as private key")
|
||||||
}
|
}
|
||||||
|
|
||||||
z, _ := priv.PublicKey.Curve.ScalarMult(pub.X, pub.Y, priv.D.Bytes())
|
z, _ := priv.Curve.ScalarMult(pub.X, pub.Y, priv.D.Bytes())
|
||||||
reader := NewConcatKDF(crypto.SHA256, z.Bytes(), algID, ptyUInfo, ptyVInfo, supPubInfo, []byte{})
|
zBytes := z.Bytes()
|
||||||
|
|
||||||
|
// Note that calling z.Bytes() on a big.Int may strip leading zero bytes from
|
||||||
|
// the returned byte array. This can lead to a problem where zBytes will be
|
||||||
|
// shorter than expected which breaks the key derivation. Therefore we must pad
|
||||||
|
// to the full length of the expected coordinate here before calling the KDF.
|
||||||
|
octSize := dSize(priv.Curve)
|
||||||
|
if len(zBytes) != octSize {
|
||||||
|
zBytes = append(bytes.Repeat([]byte{0}, octSize-len(zBytes)), zBytes...)
|
||||||
|
}
|
||||||
|
|
||||||
|
reader := NewConcatKDF(crypto.SHA256, zBytes, algID, ptyUInfo, ptyVInfo, supPubInfo, []byte{})
|
||||||
key := make([]byte, size)
|
key := make([]byte, size)
|
||||||
|
|
||||||
// Read on the KDF will never fail
|
// Read on the KDF will never fail
|
||||||
_, _ = reader.Read(key)
|
_, _ = reader.Read(key)
|
||||||
|
|
||||||
return key
|
return key
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// dSize returns the size in octets for a coordinate on a elliptic curve.
|
||||||
|
func dSize(curve elliptic.Curve) int {
|
||||||
|
order := curve.Params().P
|
||||||
|
bitLen := order.BitLen()
|
||||||
|
size := bitLen / 8
|
||||||
|
if bitLen%8 != 0 {
|
||||||
|
size++
|
||||||
|
}
|
||||||
|
return size
|
||||||
|
}
|
||||||
|
|
||||||
func lengthPrefixed(data []byte) []byte {
|
func lengthPrefixed(data []byte) []byte {
|
||||||
out := make([]byte, len(data)+4)
|
out := make([]byte, len(data)+4)
|
||||||
binary.BigEndian.PutUint32(out, uint32(len(data)))
|
binary.BigEndian.PutUint32(out, uint32(len(data)))
|
||||||
|
|
|
@ -141,6 +141,8 @@ func NewEncrypter(enc ContentEncryption, rcpt Recipient, opts *EncrypterOptions)
|
||||||
keyID, rawKey = encryptionKey.KeyID, encryptionKey.Key
|
keyID, rawKey = encryptionKey.KeyID, encryptionKey.Key
|
||||||
case *JSONWebKey:
|
case *JSONWebKey:
|
||||||
keyID, rawKey = encryptionKey.KeyID, encryptionKey.Key
|
keyID, rawKey = encryptionKey.KeyID, encryptionKey.Key
|
||||||
|
case OpaqueKeyEncrypter:
|
||||||
|
keyID, rawKey = encryptionKey.KeyID(), encryptionKey
|
||||||
default:
|
default:
|
||||||
rawKey = encryptionKey
|
rawKey = encryptionKey
|
||||||
}
|
}
|
||||||
|
@ -267,9 +269,11 @@ func makeJWERecipient(alg KeyAlgorithm, encryptionKey interface{}) (recipientKey
|
||||||
recipient, err := makeJWERecipient(alg, encryptionKey.Key)
|
recipient, err := makeJWERecipient(alg, encryptionKey.Key)
|
||||||
recipient.keyID = encryptionKey.KeyID
|
recipient.keyID = encryptionKey.KeyID
|
||||||
return recipient, err
|
return recipient, err
|
||||||
default:
|
|
||||||
return recipientKeyInfo{}, ErrUnsupportedKeyType
|
|
||||||
}
|
}
|
||||||
|
if encrypter, ok := encryptionKey.(OpaqueKeyEncrypter); ok {
|
||||||
|
return newOpaqueKeyEncrypter(alg, encrypter)
|
||||||
|
}
|
||||||
|
return recipientKeyInfo{}, ErrUnsupportedKeyType
|
||||||
}
|
}
|
||||||
|
|
||||||
// newDecrypter creates an appropriate decrypter based on the key type
|
// newDecrypter creates an appropriate decrypter based on the key type
|
||||||
|
@ -295,9 +299,11 @@ func newDecrypter(decryptionKey interface{}) (keyDecrypter, error) {
|
||||||
return newDecrypter(decryptionKey.Key)
|
return newDecrypter(decryptionKey.Key)
|
||||||
case *JSONWebKey:
|
case *JSONWebKey:
|
||||||
return newDecrypter(decryptionKey.Key)
|
return newDecrypter(decryptionKey.Key)
|
||||||
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.
|
// Implementation of encrypt method producing a JWE object.
|
||||||
|
|
|
@ -357,11 +357,11 @@ func (key rawJSONWebKey) ecPublicKey() (*ecdsa.PublicKey, error) {
|
||||||
// the curve specified in the "crv" parameter.
|
// the curve specified in the "crv" parameter.
|
||||||
// https://tools.ietf.org/html/rfc7518#section-6.2.1.2
|
// https://tools.ietf.org/html/rfc7518#section-6.2.1.2
|
||||||
if curveSize(curve) != len(key.X.data) {
|
if curveSize(curve) != len(key.X.data) {
|
||||||
return nil, fmt.Errorf("square/go-jose: invalid EC private key, wrong length for x")
|
return nil, fmt.Errorf("square/go-jose: invalid EC public key, wrong length for x")
|
||||||
}
|
}
|
||||||
|
|
||||||
if curveSize(curve) != len(key.Y.data) {
|
if curveSize(curve) != len(key.Y.data) {
|
||||||
return nil, fmt.Errorf("square/go-jose: invalid EC private key, wrong length for y")
|
return nil, fmt.Errorf("square/go-jose: invalid EC public key, wrong length for y")
|
||||||
}
|
}
|
||||||
|
|
||||||
x := key.X.bigInt()
|
x := key.X.bigInt()
|
||||||
|
|
|
@ -17,6 +17,7 @@
|
||||||
package jose
|
package jose
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
"encoding/base64"
|
"encoding/base64"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
@ -75,13 +76,21 @@ type Signature struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
// ParseSigned parses a signed message in compact or full serialization format.
|
// ParseSigned parses a signed message in compact or full serialization format.
|
||||||
func ParseSigned(input string) (*JSONWebSignature, error) {
|
func ParseSigned(signature string) (*JSONWebSignature, error) {
|
||||||
input = stripWhitespace(input)
|
signature = stripWhitespace(signature)
|
||||||
if strings.HasPrefix(input, "{") {
|
if strings.HasPrefix(signature, "{") {
|
||||||
return parseSignedFull(input)
|
return parseSignedFull(signature)
|
||||||
}
|
}
|
||||||
|
|
||||||
return parseSignedCompact(input)
|
return parseSignedCompact(signature, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ParseDetached parses a signed message in compact serialization format with detached payload.
|
||||||
|
func ParseDetached(signature string, payload []byte) (*JSONWebSignature, error) {
|
||||||
|
if payload == nil {
|
||||||
|
return nil, errors.New("square/go-jose: nil payload")
|
||||||
|
}
|
||||||
|
return parseSignedCompact(stripWhitespace(signature), payload)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get a header value
|
// Get a header value
|
||||||
|
@ -94,19 +103,38 @@ func (sig Signature) mergedHeaders() rawHeader {
|
||||||
|
|
||||||
// Compute data to be signed
|
// Compute data to be signed
|
||||||
func (obj JSONWebSignature) computeAuthData(payload []byte, signature *Signature) []byte {
|
func (obj JSONWebSignature) computeAuthData(payload []byte, signature *Signature) []byte {
|
||||||
var serializedProtected string
|
var authData bytes.Buffer
|
||||||
|
|
||||||
|
protectedHeader := new(rawHeader)
|
||||||
|
|
||||||
if signature.original != nil && signature.original.Protected != nil {
|
if signature.original != nil && signature.original.Protected != nil {
|
||||||
serializedProtected = signature.original.Protected.base64()
|
if err := json.Unmarshal(signature.original.Protected.bytes(), protectedHeader); err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
authData.WriteString(signature.original.Protected.base64())
|
||||||
} else if signature.protected != nil {
|
} else if signature.protected != nil {
|
||||||
serializedProtected = base64.RawURLEncoding.EncodeToString(mustSerializeJSON(signature.protected))
|
protectedHeader = signature.protected
|
||||||
} else {
|
authData.WriteString(base64.RawURLEncoding.EncodeToString(mustSerializeJSON(protectedHeader)))
|
||||||
serializedProtected = ""
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return []byte(fmt.Sprintf("%s.%s",
|
needsBase64 := true
|
||||||
serializedProtected,
|
|
||||||
base64.RawURLEncoding.EncodeToString(payload)))
|
if protectedHeader != nil {
|
||||||
|
var err error
|
||||||
|
if needsBase64, err = protectedHeader.getB64(); err != nil {
|
||||||
|
needsBase64 = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
authData.WriteByte('.')
|
||||||
|
|
||||||
|
if needsBase64 {
|
||||||
|
authData.WriteString(base64.RawURLEncoding.EncodeToString(payload))
|
||||||
|
} else {
|
||||||
|
authData.Write(payload)
|
||||||
|
}
|
||||||
|
|
||||||
|
return authData.Bytes()
|
||||||
}
|
}
|
||||||
|
|
||||||
// parseSignedFull parses a message in full format.
|
// parseSignedFull parses a message in full format.
|
||||||
|
@ -246,20 +274,26 @@ func (parsed *rawJSONWebSignature) sanitized() (*JSONWebSignature, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// parseSignedCompact parses a message in compact format.
|
// parseSignedCompact parses a message in compact format.
|
||||||
func parseSignedCompact(input string) (*JSONWebSignature, error) {
|
func parseSignedCompact(input string, payload []byte) (*JSONWebSignature, error) {
|
||||||
parts := strings.Split(input, ".")
|
parts := strings.Split(input, ".")
|
||||||
if len(parts) != 3 {
|
if len(parts) != 3 {
|
||||||
return nil, fmt.Errorf("square/go-jose: compact JWS format must have three parts")
|
return nil, fmt.Errorf("square/go-jose: compact JWS format must have three parts")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if parts[1] != "" && payload != nil {
|
||||||
|
return nil, fmt.Errorf("square/go-jose: payload is not detached")
|
||||||
|
}
|
||||||
|
|
||||||
rawProtected, err := base64.RawURLEncoding.DecodeString(parts[0])
|
rawProtected, err := base64.RawURLEncoding.DecodeString(parts[0])
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
payload, err := base64.RawURLEncoding.DecodeString(parts[1])
|
if payload == nil {
|
||||||
if err != nil {
|
payload, err = base64.RawURLEncoding.DecodeString(parts[1])
|
||||||
return nil, err
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
signature, err := base64.RawURLEncoding.DecodeString(parts[2])
|
signature, err := base64.RawURLEncoding.DecodeString(parts[2])
|
||||||
|
@ -275,19 +309,30 @@ func parseSignedCompact(input string) (*JSONWebSignature, error) {
|
||||||
return raw.sanitized()
|
return raw.sanitized()
|
||||||
}
|
}
|
||||||
|
|
||||||
// CompactSerialize serializes an object using the compact serialization format.
|
func (obj JSONWebSignature) compactSerialize(detached bool) (string, error) {
|
||||||
func (obj JSONWebSignature) CompactSerialize() (string, error) {
|
|
||||||
if len(obj.Signatures) != 1 || obj.Signatures[0].header != nil || obj.Signatures[0].protected == nil {
|
if len(obj.Signatures) != 1 || obj.Signatures[0].header != nil || obj.Signatures[0].protected == nil {
|
||||||
return "", ErrNotSupported
|
return "", ErrNotSupported
|
||||||
}
|
}
|
||||||
|
|
||||||
serializedProtected := mustSerializeJSON(obj.Signatures[0].protected)
|
serializedProtected := base64.RawURLEncoding.EncodeToString(mustSerializeJSON(obj.Signatures[0].protected))
|
||||||
|
payload := ""
|
||||||
|
signature := base64.RawURLEncoding.EncodeToString(obj.Signatures[0].Signature)
|
||||||
|
|
||||||
return fmt.Sprintf(
|
if !detached {
|
||||||
"%s.%s.%s",
|
payload = base64.RawURLEncoding.EncodeToString(obj.payload)
|
||||||
base64.RawURLEncoding.EncodeToString(serializedProtected),
|
}
|
||||||
base64.RawURLEncoding.EncodeToString(obj.payload),
|
|
||||||
base64.RawURLEncoding.EncodeToString(obj.Signatures[0].Signature)), nil
|
return fmt.Sprintf("%s.%s.%s", serializedProtected, payload, signature), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// CompactSerialize serializes an object using the compact serialization format.
|
||||||
|
func (obj JSONWebSignature) CompactSerialize() (string, error) {
|
||||||
|
return obj.compactSerialize(false)
|
||||||
|
}
|
||||||
|
|
||||||
|
// DetachedCompactSerialize serializes an object using the compact serialization format with detached payload.
|
||||||
|
func (obj JSONWebSignature) DetachedCompactSerialize() (string, error) {
|
||||||
|
return obj.compactSerialize(true)
|
||||||
}
|
}
|
||||||
|
|
||||||
// FullSerialize serializes an object using the full JSON serialization format.
|
// FullSerialize serializes an object using the full JSON serialization format.
|
||||||
|
|
|
@ -81,3 +81,64 @@ type opaqueVerifier struct {
|
||||||
func (o *opaqueVerifier) verifyPayload(payload []byte, signature []byte, alg SignatureAlgorithm) error {
|
func (o *opaqueVerifier) verifyPayload(payload []byte, signature []byte, alg SignatureAlgorithm) error {
|
||||||
return o.verifier.VerifyPayload(payload, signature, alg)
|
return o.verifier.VerifyPayload(payload, signature, alg)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// OpaqueKeyEncrypter is an interface that supports encrypting keys with an opaque key.
|
||||||
|
type OpaqueKeyEncrypter interface {
|
||||||
|
// KeyID returns the kid
|
||||||
|
KeyID() string
|
||||||
|
// Algs returns a list of supported key encryption algorithms.
|
||||||
|
Algs() []KeyAlgorithm
|
||||||
|
// encryptKey encrypts the CEK using the given algorithm.
|
||||||
|
encryptKey(cek []byte, alg KeyAlgorithm) (recipientInfo, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
type opaqueKeyEncrypter struct {
|
||||||
|
encrypter OpaqueKeyEncrypter
|
||||||
|
}
|
||||||
|
|
||||||
|
func newOpaqueKeyEncrypter(alg KeyAlgorithm, encrypter OpaqueKeyEncrypter) (recipientKeyInfo, error) {
|
||||||
|
var algSupported bool
|
||||||
|
for _, salg := range encrypter.Algs() {
|
||||||
|
if alg == salg {
|
||||||
|
algSupported = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !algSupported {
|
||||||
|
return recipientKeyInfo{}, ErrUnsupportedAlgorithm
|
||||||
|
}
|
||||||
|
|
||||||
|
return recipientKeyInfo{
|
||||||
|
keyID: encrypter.KeyID(),
|
||||||
|
keyAlg: alg,
|
||||||
|
keyEncrypter: &opaqueKeyEncrypter{
|
||||||
|
encrypter: encrypter,
|
||||||
|
},
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (oke *opaqueKeyEncrypter) encryptKey(cek []byte, alg KeyAlgorithm) (recipientInfo, error) {
|
||||||
|
return oke.encrypter.encryptKey(cek, alg)
|
||||||
|
}
|
||||||
|
|
||||||
|
//OpaqueKeyDecrypter is an interface that supports decrypting keys with an opaque key.
|
||||||
|
type OpaqueKeyDecrypter interface {
|
||||||
|
DecryptKey(encryptedKey []byte, header Header) ([]byte, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
type opaqueKeyDecrypter struct {
|
||||||
|
decrypter OpaqueKeyDecrypter
|
||||||
|
}
|
||||||
|
|
||||||
|
func (okd *opaqueKeyDecrypter) decryptKey(headers rawHeader, recipient *recipientInfo, generator keyGenerator) ([]byte, error) {
|
||||||
|
mergedHeaders := rawHeader{}
|
||||||
|
mergedHeaders.merge(&headers)
|
||||||
|
mergedHeaders.merge(recipient.header)
|
||||||
|
|
||||||
|
header, err := mergedHeaders.sanitized()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return okd.decrypter.DecryptKey(recipient.encryptedKey, header)
|
||||||
|
}
|
||||||
|
|
|
@ -153,12 +153,18 @@ const (
|
||||||
headerJWK = "jwk" // *JSONWebKey
|
headerJWK = "jwk" // *JSONWebKey
|
||||||
headerKeyID = "kid" // string
|
headerKeyID = "kid" // string
|
||||||
headerNonce = "nonce" // string
|
headerNonce = "nonce" // string
|
||||||
|
headerB64 = "b64" // bool
|
||||||
|
|
||||||
headerP2C = "p2c" // *byteBuffer (int)
|
headerP2C = "p2c" // *byteBuffer (int)
|
||||||
headerP2S = "p2s" // *byteBuffer ([]byte)
|
headerP2S = "p2s" // *byteBuffer ([]byte)
|
||||||
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// supportedCritical is the set of supported extensions that are understood and processed.
|
||||||
|
var supportedCritical = map[string]bool{
|
||||||
|
headerB64: true,
|
||||||
|
}
|
||||||
|
|
||||||
// rawHeader represents the JOSE header for JWE/JWS objects (used for parsing).
|
// rawHeader represents the JOSE header for JWE/JWS objects (used for parsing).
|
||||||
//
|
//
|
||||||
// The decoding of the constituent items is deferred because we want to marshal
|
// The decoding of the constituent items is deferred because we want to marshal
|
||||||
|
@ -349,6 +355,21 @@ func (parsed rawHeader) getP2S() (*byteBuffer, error) {
|
||||||
return parsed.getByteBuffer(headerP2S)
|
return parsed.getByteBuffer(headerP2S)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// getB64 extracts parsed "b64" from the raw JSON, defaulting to true.
|
||||||
|
func (parsed rawHeader) getB64() (bool, error) {
|
||||||
|
v := parsed[headerB64]
|
||||||
|
if v == nil {
|
||||||
|
return true, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var b64 bool
|
||||||
|
err := json.Unmarshal(*v, &b64)
|
||||||
|
if err != nil {
|
||||||
|
return true, err
|
||||||
|
}
|
||||||
|
return b64, nil
|
||||||
|
}
|
||||||
|
|
||||||
// sanitized produces a cleaned-up header object from the raw JSON.
|
// sanitized produces a cleaned-up header object from the raw JSON.
|
||||||
func (parsed rawHeader) sanitized() (h Header, err error) {
|
func (parsed rawHeader) sanitized() (h Header, err error) {
|
||||||
for k, v := range parsed {
|
for k, v := range parsed {
|
||||||
|
|
|
@ -17,6 +17,7 @@
|
||||||
package jose
|
package jose
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
"crypto/ecdsa"
|
"crypto/ecdsa"
|
||||||
"crypto/rsa"
|
"crypto/rsa"
|
||||||
"encoding/base64"
|
"encoding/base64"
|
||||||
|
@ -77,6 +78,27 @@ func (so *SignerOptions) WithType(typ ContentType) *SignerOptions {
|
||||||
return so.WithHeader(HeaderType, typ)
|
return so.WithHeader(HeaderType, typ)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// WithCritical adds the given names to the critical ("crit") header and returns
|
||||||
|
// the updated SignerOptions.
|
||||||
|
func (so *SignerOptions) WithCritical(names ...string) *SignerOptions {
|
||||||
|
if so.ExtraHeaders[headerCritical] == nil {
|
||||||
|
so.WithHeader(headerCritical, make([]string, 0, len(names)))
|
||||||
|
}
|
||||||
|
crit := so.ExtraHeaders[headerCritical].([]string)
|
||||||
|
so.ExtraHeaders[headerCritical] = append(crit, names...)
|
||||||
|
return so
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithBase64 adds a base64url-encode payload ("b64") header and returns the updated
|
||||||
|
// SignerOptions. When the "b64" value is "false", the payload is not base64 encoded.
|
||||||
|
func (so *SignerOptions) WithBase64(b64 bool) *SignerOptions {
|
||||||
|
if !b64 {
|
||||||
|
so.WithHeader(headerB64, b64)
|
||||||
|
so.WithCritical(headerB64)
|
||||||
|
}
|
||||||
|
return so
|
||||||
|
}
|
||||||
|
|
||||||
type payloadSigner interface {
|
type payloadSigner interface {
|
||||||
signPayload(payload []byte, alg SignatureAlgorithm) (Signature, error)
|
signPayload(payload []byte, alg SignatureAlgorithm) (Signature, error)
|
||||||
}
|
}
|
||||||
|
@ -233,7 +255,10 @@ func (ctx *genericSigner) Sign(payload []byte) (*JSONWebSignature, error) {
|
||||||
if ctx.embedJWK {
|
if ctx.embedJWK {
|
||||||
protected[headerJWK] = recipient.publicKey()
|
protected[headerJWK] = recipient.publicKey()
|
||||||
} else {
|
} else {
|
||||||
protected[headerKeyID] = recipient.publicKey().KeyID
|
keyID := recipient.publicKey().KeyID
|
||||||
|
if keyID != "" {
|
||||||
|
protected[headerKeyID] = keyID
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -250,12 +275,26 @@ func (ctx *genericSigner) Sign(payload []byte) (*JSONWebSignature, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
serializedProtected := mustSerializeJSON(protected)
|
serializedProtected := mustSerializeJSON(protected)
|
||||||
|
needsBase64 := true
|
||||||
|
|
||||||
input := []byte(fmt.Sprintf("%s.%s",
|
if b64, ok := protected[headerB64]; ok {
|
||||||
base64.RawURLEncoding.EncodeToString(serializedProtected),
|
if needsBase64, ok = b64.(bool); !ok {
|
||||||
base64.RawURLEncoding.EncodeToString(payload)))
|
return nil, errors.New("square/go-jose: Invalid b64 header parameter")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
signatureInfo, err := recipient.signer.signPayload(input, recipient.sigAlg)
|
var input bytes.Buffer
|
||||||
|
|
||||||
|
input.WriteString(base64.RawURLEncoding.EncodeToString(serializedProtected))
|
||||||
|
input.WriteByte('.')
|
||||||
|
|
||||||
|
if needsBase64 {
|
||||||
|
input.WriteString(base64.RawURLEncoding.EncodeToString(payload))
|
||||||
|
} else {
|
||||||
|
input.Write(payload)
|
||||||
|
}
|
||||||
|
|
||||||
|
signatureInfo, err := recipient.signer.signPayload(input.Bytes(), recipient.sigAlg)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -324,9 +363,11 @@ func (obj JSONWebSignature) DetachedVerify(payload []byte, verificationKey inter
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if len(critical) > 0 {
|
|
||||||
// Unsupported crit header
|
for _, name := range critical {
|
||||||
return ErrCryptoFailure
|
if !supportedCritical[name] {
|
||||||
|
return ErrCryptoFailure
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
input := obj.computeAuthData(payload, &signature)
|
input := obj.computeAuthData(payload, &signature)
|
||||||
|
@ -366,15 +407,18 @@ func (obj JSONWebSignature) DetachedVerifyMulti(payload []byte, verificationKey
|
||||||
return -1, Signature{}, err
|
return -1, Signature{}, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
outer:
|
||||||
for i, signature := range obj.Signatures {
|
for i, signature := range obj.Signatures {
|
||||||
headers := signature.mergedHeaders()
|
headers := signature.mergedHeaders()
|
||||||
critical, err := headers.getCritical()
|
critical, err := headers.getCritical()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if len(critical) > 0 {
|
|
||||||
// Unsupported crit header
|
for _, name := range critical {
|
||||||
continue
|
if !supportedCritical[name] {
|
||||||
|
continue outer
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
input := obj.computeAuthData(payload, &signature)
|
input := obj.computeAuthData(payload, &signature)
|
||||||
|
|
|
@ -167,7 +167,7 @@ google.golang.org/grpc/tap
|
||||||
gopkg.in/fsnotify.v1
|
gopkg.in/fsnotify.v1
|
||||||
# gopkg.in/go-gorp/gorp.v2 v2.0.0-20180410155428-6032c66e0f5f
|
# gopkg.in/go-gorp/gorp.v2 v2.0.0-20180410155428-6032c66e0f5f
|
||||||
gopkg.in/go-gorp/gorp.v2
|
gopkg.in/go-gorp/gorp.v2
|
||||||
# gopkg.in/square/go-jose.v2 v2.3.1
|
# gopkg.in/square/go-jose.v2 v2.4.0
|
||||||
gopkg.in/square/go-jose.v2
|
gopkg.in/square/go-jose.v2
|
||||||
gopkg.in/square/go-jose.v2/cipher
|
gopkg.in/square/go-jose.v2/cipher
|
||||||
gopkg.in/square/go-jose.v2/json
|
gopkg.in/square/go-jose.v2/json
|
||||||
|
|
|
@ -315,12 +315,7 @@ func (wfe *WebFrontEndImpl) parseJWS(body []byte) (*jose.JSONWebSignature, *prob
|
||||||
parsedJWS, err := jose.ParseSigned(bodyStr)
|
parsedJWS, err := jose.ParseSigned(bodyStr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
wfe.stats.joseErrorCount.With(prometheus.Labels{"type": "JWSParseError"}).Inc()
|
wfe.stats.joseErrorCount.With(prometheus.Labels{"type": "JWSParseError"}).Inc()
|
||||||
// TODO(#4300): This error references private keys, while the issue is
|
if strings.HasPrefix(err.Error(), "failed to unmarshal JWK: square/go-jose: invalid EC public key, wrong length") {
|
||||||
// actually with public keys. A fix has been merged upstream but we
|
|
||||||
// are waiting until a tagged release containing the change is made.
|
|
||||||
// Once there is a release containing the fix we'll need to update
|
|
||||||
// the dep and fix this check.
|
|
||||||
if strings.HasPrefix(err.Error(), "failed to unmarshal JWK: square/go-jose: invalid EC private key, wrong length") {
|
|
||||||
return nil, probs.Malformed("Parse error reading JWS: EC public key has incorrect padding")
|
return nil, probs.Malformed("Parse error reading JWS: EC public key has incorrect padding")
|
||||||
}
|
}
|
||||||
return nil, probs.Malformed("Parse error reading JWS")
|
return nil, probs.Malformed("Parse error reading JWS")
|
||||||
|
|
Loading…
Reference in New Issue