diff options
Diffstat (limited to 'vendor/gopkg.in/square/go-jose.v2')
-rw-r--r-- | vendor/gopkg.in/square/go-jose.v2/.gitignore | 1 | ||||
-rw-r--r-- | vendor/gopkg.in/square/go-jose.v2/.travis.yml | 11 | ||||
-rw-r--r-- | vendor/gopkg.in/square/go-jose.v2/asymmetric.go | 4 | ||||
-rw-r--r-- | vendor/gopkg.in/square/go-jose.v2/cipher/ecdh_es.go | 28 | ||||
-rw-r--r-- | vendor/gopkg.in/square/go-jose.v2/crypter.go | 14 | ||||
-rw-r--r-- | vendor/gopkg.in/square/go-jose.v2/encoding.go | 14 | ||||
-rw-r--r-- | vendor/gopkg.in/square/go-jose.v2/jwk.go | 174 | ||||
-rw-r--r-- | vendor/gopkg.in/square/go-jose.v2/jws.go | 95 | ||||
-rw-r--r-- | vendor/gopkg.in/square/go-jose.v2/opaque.go | 61 | ||||
-rw-r--r-- | vendor/gopkg.in/square/go-jose.v2/shared.go | 21 | ||||
-rw-r--r-- | vendor/gopkg.in/square/go-jose.v2/signing.go | 74 |
11 files changed, 432 insertions, 65 deletions
diff --git a/vendor/gopkg.in/square/go-jose.v2/.gitignore b/vendor/gopkg.in/square/go-jose.v2/.gitignore index 5b4d73b68..95a851586 100644 --- a/vendor/gopkg.in/square/go-jose.v2/.gitignore +++ b/vendor/gopkg.in/square/go-jose.v2/.gitignore @@ -5,3 +5,4 @@ *.pem *.cov jose-util/jose-util +jose-util.t.err
\ No newline at end of file diff --git a/vendor/gopkg.in/square/go-jose.v2/.travis.yml b/vendor/gopkg.in/square/go-jose.v2/.travis.yml index fc501ca9b..ae69862df 100644 --- a/vendor/gopkg.in/square/go-jose.v2/.travis.yml +++ b/vendor/gopkg.in/square/go-jose.v2/.travis.yml @@ -8,11 +8,9 @@ matrix: - go: tip go: -- '1.7.x' -- '1.8.x' -- '1.9.x' -- '1.10.x' - '1.11.x' +- '1.12.x' +- tip go_import_path: gopkg.in/square/go-jose.v2 @@ -28,6 +26,8 @@ before_install: - go get github.com/wadey/gocovmerge - go get github.com/mattn/goveralls - go get github.com/stretchr/testify/assert +- go get github.com/stretchr/testify/require +- go get github.com/google/go-cmp/cmp - go get golang.org/x/tools/cmd/cover || true - go get code.google.com/p/go.tools/cmd/cover || true - pip install cram --user @@ -37,10 +37,9 @@ script: - go test ./cipher -v -covermode=count -coverprofile=cipher/profile.cov - go test ./jwt -v -covermode=count -coverprofile=jwt/profile.cov - go test ./json -v # no coverage for forked encoding/json package -- cd jose-util && go build && PATH=$PWD:$PATH cram -v jose-util.t +- cd jose-util && go build && PATH=$PWD:$PATH cram -v jose-util.t # cram tests jose-util - cd .. after_success: - gocovmerge *.cov */*.cov > merged.coverprofile - $HOME/gopath/bin/goveralls -coverprofile merged.coverprofile -service=travis-ci - diff --git a/vendor/gopkg.in/square/go-jose.v2/asymmetric.go b/vendor/gopkg.in/square/go-jose.v2/asymmetric.go index 67935561b..b69aa0369 100644 --- a/vendor/gopkg.in/square/go-jose.v2/asymmetric.go +++ b/vendor/gopkg.in/square/go-jose.v2/asymmetric.go @@ -29,7 +29,7 @@ import ( "math/big" "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" ) @@ -288,7 +288,7 @@ func (ctx rsaDecrypterSigner) signPayload(payload []byte, alg SignatureAlgorithm out, err = rsa.SignPKCS1v15(RandReader, ctx.privateKey, hash, hashed) case PS256, PS384, PS512: out, err = rsa.SignPSS(RandReader, ctx.privateKey, hash, hashed, &rsa.PSSOptions{ - SaltLength: rsa.PSSSaltLengthAuto, + SaltLength: rsa.PSSSaltLengthEqualsHash, }) } diff --git a/vendor/gopkg.in/square/go-jose.v2/cipher/ecdh_es.go b/vendor/gopkg.in/square/go-jose.v2/cipher/ecdh_es.go index c128e327f..093c64674 100644 --- a/vendor/gopkg.in/square/go-jose.v2/cipher/ecdh_es.go +++ b/vendor/gopkg.in/square/go-jose.v2/cipher/ecdh_es.go @@ -17,8 +17,10 @@ package josecipher import ( + "bytes" "crypto" "crypto/ecdsa" + "crypto/elliptic" "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") } - z, _ := priv.PublicKey.Curve.ScalarMult(pub.X, pub.Y, priv.D.Bytes()) - reader := NewConcatKDF(crypto.SHA256, z.Bytes(), algID, ptyUInfo, ptyVInfo, supPubInfo, []byte{}) + z, _ := priv.Curve.ScalarMult(pub.X, pub.Y, priv.D.Bytes()) + 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) // Read on the KDF will never fail _, _ = reader.Read(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 { out := make([]byte, len(data)+4) binary.BigEndian.PutUint32(out, uint32(len(data))) diff --git a/vendor/gopkg.in/square/go-jose.v2/crypter.go b/vendor/gopkg.in/square/go-jose.v2/crypter.go index c45c71206..d24cabf6b 100644 --- a/vendor/gopkg.in/square/go-jose.v2/crypter.go +++ b/vendor/gopkg.in/square/go-jose.v2/crypter.go @@ -141,6 +141,8 @@ func NewEncrypter(enc ContentEncryption, rcpt Recipient, opts *EncrypterOptions) keyID, rawKey = encryptionKey.KeyID, encryptionKey.Key case *JSONWebKey: keyID, rawKey = encryptionKey.KeyID, encryptionKey.Key + case OpaqueKeyEncrypter: + keyID, rawKey = encryptionKey.KeyID(), encryptionKey default: rawKey = encryptionKey } @@ -267,9 +269,11 @@ func makeJWERecipient(alg KeyAlgorithm, encryptionKey interface{}) (recipientKey recipient, err := makeJWERecipient(alg, encryptionKey.Key) recipient.keyID = encryptionKey.KeyID 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 @@ -295,9 +299,11 @@ func newDecrypter(decryptionKey interface{}) (keyDecrypter, error) { return newDecrypter(decryptionKey.Key) case *JSONWebKey: 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. diff --git a/vendor/gopkg.in/square/go-jose.v2/encoding.go b/vendor/gopkg.in/square/go-jose.v2/encoding.go index b9687c647..70f7385c4 100644 --- a/vendor/gopkg.in/square/go-jose.v2/encoding.go +++ b/vendor/gopkg.in/square/go-jose.v2/encoding.go @@ -23,13 +23,12 @@ import ( "encoding/binary" "io" "math/big" - "regexp" + "strings" + "unicode" "gopkg.in/square/go-jose.v2/json" ) -var stripWhitespaceRegex = regexp.MustCompile("\\s") - // Helper function to serialize known-good objects. // Precondition: value is not a nil pointer. func mustSerializeJSON(value interface{}) []byte { @@ -56,7 +55,14 @@ func mustSerializeJSON(value interface{}) []byte { // Strip all newlines and whitespace func stripWhitespace(data string) string { - return stripWhitespaceRegex.ReplaceAllString(data, "") + buf := strings.Builder{} + buf.Grow(len(data)) + for _, r := range data { + if !unicode.IsSpace(r) { + buf.WriteRune(r) + } + } + return buf.String() } // Perform compression based on algorithm diff --git a/vendor/gopkg.in/square/go-jose.v2/jwk.go b/vendor/gopkg.in/square/go-jose.v2/jwk.go index 6cb8adb84..2dc6aec4b 100644 --- a/vendor/gopkg.in/square/go-jose.v2/jwk.go +++ b/vendor/gopkg.in/square/go-jose.v2/jwk.go @@ -17,15 +17,20 @@ package jose import ( + "bytes" "crypto" "crypto/ecdsa" "crypto/elliptic" "crypto/rsa" + "crypto/sha1" + "crypto/sha256" "crypto/x509" "encoding/base64" + "encoding/hex" "errors" "fmt" "math/big" + "net/url" "reflect" "strings" @@ -57,16 +62,31 @@ type rawJSONWebKey struct { Dq *byteBuffer `json:"dq,omitempty"` Qi *byteBuffer `json:"qi,omitempty"` // Certificates - X5c []string `json:"x5c,omitempty"` + X5c []string `json:"x5c,omitempty"` + X5u *url.URL `json:"x5u,omitempty"` + X5tSHA1 string `json:"x5t,omitempty"` + X5tSHA256 string `json:"x5t#S256,omitempty"` } // JSONWebKey represents a public or private key in JWK format. type JSONWebKey struct { - Key interface{} + // Cryptographic key, can be a symmetric or asymmetric key. + Key interface{} + // Key identifier, parsed from `kid` header. + KeyID string + // Key algorithm, parsed from `alg` header. + Algorithm string + // Key use, parsed from `use` header. + Use string + + // X.509 certificate chain, parsed from `x5c` header. Certificates []*x509.Certificate - KeyID string - Algorithm string - Use string + // X.509 certificate URL, parsed from `x5u` header. + CertificatesURL *url.URL + // X.509 certificate thumbprint (SHA-1), parsed from `x5t` header. + CertificateThumbprintSHA1 []byte + // X.509 certificate thumbprint (SHA-256), parsed from `x5t#S256` header. + CertificateThumbprintSHA256 []byte } // MarshalJSON serializes the given key to its JSON representation. @@ -105,6 +125,39 @@ func (k JSONWebKey) MarshalJSON() ([]byte, error) { raw.X5c = append(raw.X5c, base64.StdEncoding.EncodeToString(cert.Raw)) } + x5tSHA1Len := len(k.CertificateThumbprintSHA1) + x5tSHA256Len := len(k.CertificateThumbprintSHA256) + if x5tSHA1Len > 0 { + if x5tSHA1Len != sha1.Size { + return nil, fmt.Errorf("square/go-jose: invalid SHA-1 thumbprint (must be %d bytes, not %d)", sha1.Size, x5tSHA1Len) + } + raw.X5tSHA1 = base64.RawURLEncoding.EncodeToString(k.CertificateThumbprintSHA1) + } + if x5tSHA256Len > 0 { + if x5tSHA256Len != sha256.Size { + return nil, fmt.Errorf("square/go-jose: invalid SHA-256 thumbprint (must be %d bytes, not %d)", sha256.Size, x5tSHA256Len) + } + raw.X5tSHA256 = base64.RawURLEncoding.EncodeToString(k.CertificateThumbprintSHA256) + } + + // If cert chain is attached (as opposed to being behind a URL), check the + // keys thumbprints to make sure they match what is expected. This is to + // ensure we don't accidentally produce a JWK with semantically inconsistent + // data in the headers. + if len(k.Certificates) > 0 { + expectedSHA1 := sha1.Sum(k.Certificates[0].Raw) + expectedSHA256 := sha256.Sum256(k.Certificates[0].Raw) + + if len(k.CertificateThumbprintSHA1) > 0 && !bytes.Equal(k.CertificateThumbprintSHA1, expectedSHA1[:]) { + return nil, errors.New("square/go-jose: invalid SHA-1 thumbprint, does not match cert chain") + } + if len(k.CertificateThumbprintSHA256) > 0 && !bytes.Equal(k.CertificateThumbprintSHA256, expectedSHA256[:]) { + return nil, errors.New("square/go-jose: invalid or SHA-256 thumbprint, does not match cert chain") + } + } + + raw.X5u = k.CertificatesURL + return json.Marshal(raw) } @@ -116,28 +169,61 @@ func (k *JSONWebKey) UnmarshalJSON(data []byte) (err error) { return err } + certs, err := parseCertificateChain(raw.X5c) + if err != nil { + return fmt.Errorf("square/go-jose: failed to unmarshal x5c field: %s", err) + } + var key interface{} + var certPub interface{} + var keyPub interface{} + + if len(certs) > 0 { + // We need to check that leaf public key matches the key embedded in this + // JWK, as required by the standard (see RFC 7517, Section 4.7). Otherwise + // the JWK parsed could be semantically invalid. Technically, should also + // check key usage fields and other extensions on the cert here, but the + // standard doesn't exactly explain how they're supposed to map from the + // JWK representation to the X.509 extensions. + certPub = certs[0].PublicKey + } + switch raw.Kty { case "EC": if raw.D != nil { key, err = raw.ecPrivateKey() + if err == nil { + keyPub = key.(*ecdsa.PrivateKey).Public() + } } else { key, err = raw.ecPublicKey() + keyPub = key } case "RSA": if raw.D != nil { key, err = raw.rsaPrivateKey() + if err == nil { + keyPub = key.(*rsa.PrivateKey).Public() + } } else { key, err = raw.rsaPublicKey() + keyPub = key } case "oct": + if certPub != nil { + return errors.New("square/go-jose: invalid JWK, found 'oct' (symmetric) key with cert chain") + } key, err = raw.symmetricKey() case "OKP": if raw.Crv == "Ed25519" && raw.X != nil { if raw.D != nil { key, err = raw.edPrivateKey() + if err == nil { + keyPub = key.(ed25519.PrivateKey).Public() + } } else { key, err = raw.edPublicKey() + keyPub = key } } else { err = fmt.Errorf("square/go-jose: unknown curve %s'", raw.Crv) @@ -146,12 +232,78 @@ func (k *JSONWebKey) UnmarshalJSON(data []byte) (err error) { err = fmt.Errorf("square/go-jose: unknown json web key type '%s'", raw.Kty) } - if err == nil { - *k = JSONWebKey{Key: key, KeyID: raw.Kid, Algorithm: raw.Alg, Use: raw.Use} + if err != nil { + return + } + + if certPub != nil && keyPub != nil { + if !reflect.DeepEqual(certPub, keyPub) { + return errors.New("square/go-jose: invalid JWK, public keys in key and x5c fields to not match") + } + } + + *k = JSONWebKey{Key: key, KeyID: raw.Kid, Algorithm: raw.Alg, Use: raw.Use, Certificates: certs} - k.Certificates, err = parseCertificateChain(raw.X5c) + k.CertificatesURL = raw.X5u + + // x5t parameters are base64url-encoded SHA thumbprints + // See RFC 7517, Section 4.8, https://tools.ietf.org/html/rfc7517#section-4.8 + x5tSHA1bytes, err := base64.RawURLEncoding.DecodeString(raw.X5tSHA1) + if err != nil { + return errors.New("square/go-jose: invalid JWK, x5t header has invalid encoding") + } + + // RFC 7517, Section 4.8 is ambiguous as to whether the digest output should be byte or hex, + // for this reason, after base64 decoding, if the size is sha1.Size it's likely that the value is a byte encoded + // checksum so we skip this. Otherwise if the checksum was hex encoded we expect a 40 byte sized array so we'll + // try to hex decode it. When Marshalling this value we'll always use a base64 encoded version of byte format checksum. + if len(x5tSHA1bytes) == 2*sha1.Size { + hx, err := hex.DecodeString(string(x5tSHA1bytes)) + if err != nil { + return fmt.Errorf("square/go-jose: invalid JWK, unable to hex decode x5t: %v", err) + + } + x5tSHA1bytes = hx + } + + k.CertificateThumbprintSHA1 = x5tSHA1bytes + + x5tSHA256bytes, err := base64.RawURLEncoding.DecodeString(raw.X5tSHA256) + if err != nil { + return errors.New("square/go-jose: invalid JWK, x5t#S256 header has invalid encoding") + } + + if len(x5tSHA256bytes) == 2*sha256.Size { + hx256, err := hex.DecodeString(string(x5tSHA256bytes)) if err != nil { - return fmt.Errorf("failed to unmarshal x5c field: %s", err) + return fmt.Errorf("square/go-jose: invalid JWK, unable to hex decode x5t#S256: %v", err) + } + x5tSHA256bytes = hx256 + } + + k.CertificateThumbprintSHA256 = x5tSHA256bytes + + x5tSHA1Len := len(k.CertificateThumbprintSHA1) + x5tSHA256Len := len(k.CertificateThumbprintSHA256) + if x5tSHA1Len > 0 && x5tSHA1Len != sha1.Size { + return errors.New("square/go-jose: invalid JWK, x5t header is of incorrect size") + } + if x5tSHA256Len > 0 && x5tSHA256Len != sha256.Size { + return errors.New("square/go-jose: invalid JWK, x5t#S256 header is of incorrect size") + } + + // If certificate chain *and* thumbprints are set, verify correctness. + if len(k.Certificates) > 0 { + leaf := k.Certificates[0] + sha1sum := sha1.Sum(leaf.Raw) + sha256sum := sha256.Sum256(leaf.Raw) + + if len(k.CertificateThumbprintSHA1) > 0 && !bytes.Equal(sha1sum[:], k.CertificateThumbprintSHA1) { + return errors.New("square/go-jose: invalid JWK, x5c thumbprint does not match x5t value") + } + + if len(k.CertificateThumbprintSHA256) > 0 && !bytes.Equal(sha256sum[:], k.CertificateThumbprintSHA256) { + return errors.New("square/go-jose: invalid JWK, x5c thumbprint does not match x5t#S256 value") } } @@ -357,11 +509,11 @@ func (key rawJSONWebKey) ecPublicKey() (*ecdsa.PublicKey, error) { // the curve specified in the "crv" parameter. // https://tools.ietf.org/html/rfc7518#section-6.2.1.2 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) { - 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() diff --git a/vendor/gopkg.in/square/go-jose.v2/jws.go b/vendor/gopkg.in/square/go-jose.v2/jws.go index 8b59b6ab2..7e261f937 100644 --- a/vendor/gopkg.in/square/go-jose.v2/jws.go +++ b/vendor/gopkg.in/square/go-jose.v2/jws.go @@ -17,6 +17,7 @@ package jose import ( + "bytes" "encoding/base64" "errors" "fmt" @@ -75,13 +76,21 @@ type Signature struct { } // ParseSigned parses a signed message in compact or full serialization format. -func ParseSigned(input string) (*JSONWebSignature, error) { - input = stripWhitespace(input) - if strings.HasPrefix(input, "{") { - return parseSignedFull(input) +func ParseSigned(signature string) (*JSONWebSignature, error) { + signature = stripWhitespace(signature) + if strings.HasPrefix(signature, "{") { + 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 @@ -93,20 +102,39 @@ func (sig Signature) mergedHeaders() rawHeader { } // Compute data to be signed -func (obj JSONWebSignature) computeAuthData(payload []byte, signature *Signature) []byte { - var serializedProtected string +func (obj JSONWebSignature) computeAuthData(payload []byte, signature *Signature) ([]byte, error) { + var authData bytes.Buffer + + protectedHeader := new(rawHeader) if signature.original != nil && signature.original.Protected != nil { - serializedProtected = signature.original.Protected.base64() + if err := json.Unmarshal(signature.original.Protected.bytes(), protectedHeader); err != nil { + return nil, err + } + authData.WriteString(signature.original.Protected.base64()) } else if signature.protected != nil { - serializedProtected = base64.RawURLEncoding.EncodeToString(mustSerializeJSON(signature.protected)) + protectedHeader = signature.protected + authData.WriteString(base64.RawURLEncoding.EncodeToString(mustSerializeJSON(protectedHeader))) + } + + needsBase64 := true + + 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 { - serializedProtected = "" + authData.Write(payload) } - return []byte(fmt.Sprintf("%s.%s", - serializedProtected, - base64.RawURLEncoding.EncodeToString(payload))) + return authData.Bytes(), nil } // parseSignedFull parses a message in full format. @@ -246,20 +274,26 @@ func (parsed *rawJSONWebSignature) sanitized() (*JSONWebSignature, error) { } // 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, ".") if len(parts) != 3 { 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]) if err != nil { return nil, err } - payload, err := base64.RawURLEncoding.DecodeString(parts[1]) - if err != nil { - return nil, err + if payload == nil { + payload, err = base64.RawURLEncoding.DecodeString(parts[1]) + if err != nil { + return nil, err + } } signature, err := base64.RawURLEncoding.DecodeString(parts[2]) @@ -275,19 +309,30 @@ func parseSignedCompact(input string) (*JSONWebSignature, error) { return raw.sanitized() } -// CompactSerialize serializes an object using the compact serialization format. -func (obj JSONWebSignature) CompactSerialize() (string, error) { +func (obj JSONWebSignature) compactSerialize(detached bool) (string, error) { if len(obj.Signatures) != 1 || obj.Signatures[0].header != nil || obj.Signatures[0].protected == nil { 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) + + if !detached { + payload = base64.RawURLEncoding.EncodeToString(obj.payload) + } + + 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) +} - return fmt.Sprintf( - "%s.%s.%s", - base64.RawURLEncoding.EncodeToString(serializedProtected), - base64.RawURLEncoding.EncodeToString(obj.payload), - base64.RawURLEncoding.EncodeToString(obj.Signatures[0].Signature)), nil +// 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. diff --git a/vendor/gopkg.in/square/go-jose.v2/opaque.go b/vendor/gopkg.in/square/go-jose.v2/opaque.go index 4a8bd8f32..df747f992 100644 --- a/vendor/gopkg.in/square/go-jose.v2/opaque.go +++ b/vendor/gopkg.in/square/go-jose.v2/opaque.go @@ -81,3 +81,64 @@ type opaqueVerifier struct { func (o *opaqueVerifier) verifyPayload(payload []byte, signature []byte, alg SignatureAlgorithm) error { 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) +} diff --git a/vendor/gopkg.in/square/go-jose.v2/shared.go b/vendor/gopkg.in/square/go-jose.v2/shared.go index b0a6255ec..f8438641f 100644 --- a/vendor/gopkg.in/square/go-jose.v2/shared.go +++ b/vendor/gopkg.in/square/go-jose.v2/shared.go @@ -153,12 +153,18 @@ const ( headerJWK = "jwk" // *JSONWebKey headerKeyID = "kid" // string headerNonce = "nonce" // string + headerB64 = "b64" // bool headerP2C = "p2c" // *byteBuffer (int) 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). // // 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) } +// 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. func (parsed rawHeader) sanitized() (h Header, err error) { for k, v := range parsed { diff --git a/vendor/gopkg.in/square/go-jose.v2/signing.go b/vendor/gopkg.in/square/go-jose.v2/signing.go index be6cf0481..bad820cea 100644 --- a/vendor/gopkg.in/square/go-jose.v2/signing.go +++ b/vendor/gopkg.in/square/go-jose.v2/signing.go @@ -17,6 +17,7 @@ package jose import ( + "bytes" "crypto/ecdsa" "crypto/rsa" "encoding/base64" @@ -77,6 +78,27 @@ func (so *SignerOptions) WithType(typ ContentType) *SignerOptions { 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 { signPayload(payload []byte, alg SignatureAlgorithm) (Signature, error) } @@ -233,7 +255,10 @@ func (ctx *genericSigner) Sign(payload []byte) (*JSONWebSignature, error) { if ctx.embedJWK { protected[headerJWK] = recipient.publicKey() } 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) + needsBase64 := true + + if b64, ok := protected[headerB64]; ok { + if needsBase64, ok = b64.(bool); !ok { + return nil, errors.New("square/go-jose: Invalid b64 header parameter") + } + } + + var input bytes.Buffer - input := []byte(fmt.Sprintf("%s.%s", - base64.RawURLEncoding.EncodeToString(serializedProtected), - base64.RawURLEncoding.EncodeToString(payload))) + input.WriteString(base64.RawURLEncoding.EncodeToString(serializedProtected)) + input.WriteByte('.') - signatureInfo, err := recipient.signer.signPayload(input, recipient.sigAlg) + if needsBase64 { + input.WriteString(base64.RawURLEncoding.EncodeToString(payload)) + } else { + input.Write(payload) + } + + signatureInfo, err := recipient.signer.signPayload(input.Bytes(), recipient.sigAlg) if err != nil { return nil, err } @@ -324,12 +363,18 @@ func (obj JSONWebSignature) DetachedVerify(payload []byte, verificationKey inter if err != nil { return err } - if len(critical) > 0 { - // Unsupported crit header + + for _, name := range critical { + if !supportedCritical[name] { + return ErrCryptoFailure + } + } + + input, err := obj.computeAuthData(payload, &signature) + if err != nil { return ErrCryptoFailure } - input := obj.computeAuthData(payload, &signature) alg := headers.getSignatureAlgorithm() err = verifier.verifyPayload(input, signature.Signature, alg) if err == nil { @@ -366,18 +411,25 @@ func (obj JSONWebSignature) DetachedVerifyMulti(payload []byte, verificationKey return -1, Signature{}, err } +outer: for i, signature := range obj.Signatures { headers := signature.mergedHeaders() critical, err := headers.getCritical() if err != nil { continue } - if len(critical) > 0 { - // Unsupported crit header + + for _, name := range critical { + if !supportedCritical[name] { + continue outer + } + } + + input, err := obj.computeAuthData(payload, &signature) + if err != nil { continue } - input := obj.computeAuthData(payload, &signature) alg := headers.getSignatureAlgorithm() err = verifier.verifyPayload(input, signature.Signature, alg) if err == nil { |