Merge pull request #62420 from mikedanese/jose-bump

Automatic merge from submit-queue (batch tested with PRs 67707, 62420). If you want to cherry-pick this change to another branch, please follow the instructions <a href="https://github.com/kubernetes/community/blob/master/contributors/devel/cherry-picks.md">here</a>.

svcacct: support jose.OpaqueSigner and push errors to token generator creation

```release-note
NONE
```
pull/8/head
Kubernetes Submit Queue 2018-08-23 15:39:59 -07:00 committed by GitHub
commit 166a1356e8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
17 changed files with 346 additions and 90 deletions

8
Godeps/Godeps.json generated
View File

@ -3684,22 +3684,22 @@
{
"ImportPath": "gopkg.in/square/go-jose.v2",
"Comment": "v2.1.3",
"Rev": "f8f38de21b4dcd69d0413faf231983f5fd6634b1"
"Rev": "89060dee6a84df9a4dae49f676f0c755037834f1"
},
{
"ImportPath": "gopkg.in/square/go-jose.v2/cipher",
"Comment": "v2.1.3",
"Rev": "f8f38de21b4dcd69d0413faf231983f5fd6634b1"
"Rev": "89060dee6a84df9a4dae49f676f0c755037834f1"
},
{
"ImportPath": "gopkg.in/square/go-jose.v2/json",
"Comment": "v2.1.3",
"Rev": "f8f38de21b4dcd69d0413faf231983f5fd6634b1"
"Rev": "89060dee6a84df9a4dae49f676f0c755037834f1"
},
{
"ImportPath": "gopkg.in/square/go-jose.v2/jwt",
"Comment": "v2.1.3",
"Rev": "f8f38de21b4dcd69d0413faf231983f5fd6634b1"
"Rev": "89060dee6a84df9a4dae49f676f0c755037834f1"
},
{
"ImportPath": "gopkg.in/warnings.v0",

View File

@ -360,7 +360,11 @@ func CreateKubeAPIServerConfig(
}
}
issuer = serviceaccount.JWTTokenGenerator(s.Authentication.ServiceAccounts.Issuer, sk)
issuer, err = serviceaccount.JWTTokenGenerator(s.Authentication.ServiceAccounts.Issuer, sk)
if err != nil {
lastErr = fmt.Errorf("failed to build token generator: %v", err)
return
}
apiAudiences = s.Authentication.ServiceAccounts.APIAudiences
maxExpiration = s.Authentication.ServiceAccounts.MaxExpiration
}

View File

@ -516,12 +516,16 @@ func (c serviceAccountTokenControllerStarter) startServiceAccountTokenController
rootCA = c.rootClientBuilder.ConfigOrDie("tokens-controller").CAData
}
tokenGenerator, err := serviceaccount.JWTTokenGenerator(serviceaccount.LegacyIssuer, privateKey)
if err != nil {
return nil, false, fmt.Errorf("failed to build token generator: %v", err)
}
controller, err := serviceaccountcontroller.NewTokensController(
ctx.InformerFactory.Core().V1().ServiceAccounts(),
ctx.InformerFactory.Core().V1().Secrets(),
c.rootClientBuilder.ClientOrDie("tokens-controller"),
serviceaccountcontroller.TokensControllerOptions{
TokenGenerator: serviceaccount.JWTTokenGenerator(serviceaccount.LegacyIssuer, privateKey),
TokenGenerator: tokenGenerator,
RootCA: rootCA,
},
)

View File

@ -54,25 +54,13 @@ type TokenGenerator interface {
// JWTTokenGenerator returns a TokenGenerator that generates signed JWT tokens, using the given privateKey.
// privateKey is a PEM-encoded byte array of a private RSA key.
// JWTTokenAuthenticator()
func JWTTokenGenerator(iss string, privateKey interface{}) TokenGenerator {
return &jwtTokenGenerator{
iss: iss,
privateKey: privateKey,
}
}
type jwtTokenGenerator struct {
iss string
privateKey interface{}
}
func (j *jwtTokenGenerator) GenerateToken(claims *jwt.Claims, privateClaims interface{}) (string, error) {
func JWTTokenGenerator(iss string, privateKey interface{}) (TokenGenerator, error) {
var alg jose.SignatureAlgorithm
switch privateKey := j.privateKey.(type) {
switch pk := privateKey.(type) {
case *rsa.PrivateKey:
alg = jose.RS256
case *ecdsa.PrivateKey:
switch privateKey.Curve {
switch pk.Curve {
case elliptic.P256():
alg = jose.ES256
case elliptic.P384():
@ -80,25 +68,38 @@ func (j *jwtTokenGenerator) GenerateToken(claims *jwt.Claims, privateClaims inte
case elliptic.P521():
alg = jose.ES512
default:
return "", fmt.Errorf("unknown private key curve, must be 256, 384, or 521")
return nil, fmt.Errorf("unknown private key curve, must be 256, 384, or 521")
}
case jose.OpaqueSigner:
alg = jose.SignatureAlgorithm(pk.Public().Algorithm)
default:
return "", fmt.Errorf("unknown private key type %T, must be *rsa.PrivateKey or *ecdsa.PrivateKey", j.privateKey)
return nil, fmt.Errorf("unknown private key type %T, must be *rsa.PrivateKey, *ecdsa.PrivateKey, or jose.OpaqueSigner", privateKey)
}
signer, err := jose.NewSigner(
jose.SigningKey{
Algorithm: alg,
Key: j.privateKey,
Key: privateKey,
},
nil,
)
if err != nil {
return "", err
return nil, err
}
return &jwtTokenGenerator{
iss: iss,
signer: signer,
}, nil
}
type jwtTokenGenerator struct {
iss string
signer jose.Signer
}
func (j *jwtTokenGenerator) GenerateToken(claims *jwt.Claims, privateClaims interface{}) (string, error) {
// claims are applied in reverse precedence
return jwt.Signed(signer).
return jwt.Signed(j.signer).
Claims(privateClaims).
Claims(claims).
Claims(&jwt.Claims{

View File

@ -127,7 +127,10 @@ func TestTokenGenerateAndValidate(t *testing.T) {
}
// Generate the RSA token
rsaGenerator := serviceaccount.JWTTokenGenerator(serviceaccount.LegacyIssuer, getPrivateKey(rsaPrivateKey))
rsaGenerator, err := serviceaccount.JWTTokenGenerator(serviceaccount.LegacyIssuer, getPrivateKey(rsaPrivateKey))
if err != nil {
t.Fatalf("error making generator: %v", err)
}
rsaToken, err := rsaGenerator.GenerateToken(serviceaccount.LegacyClaims(*serviceAccount, *rsaSecret))
if err != nil {
t.Fatalf("error generating token: %v", err)
@ -140,7 +143,10 @@ func TestTokenGenerateAndValidate(t *testing.T) {
}
// Generate the ECDSA token
ecdsaGenerator := serviceaccount.JWTTokenGenerator(serviceaccount.LegacyIssuer, getPrivateKey(ecdsaPrivateKey))
ecdsaGenerator, err := serviceaccount.JWTTokenGenerator(serviceaccount.LegacyIssuer, getPrivateKey(ecdsaPrivateKey))
if err != nil {
t.Fatalf("error making generator: %v", err)
}
ecdsaToken, err := ecdsaGenerator.GenerateToken(serviceaccount.LegacyClaims(*serviceAccount, *ecdsaSecret))
if err != nil {
t.Fatalf("error generating token: %v", err)
@ -153,7 +159,10 @@ func TestTokenGenerateAndValidate(t *testing.T) {
}
// Generate signer with same keys as RSA signer but different issuer
badIssuerGenerator := serviceaccount.JWTTokenGenerator("foo", getPrivateKey(rsaPrivateKey))
badIssuerGenerator, err := serviceaccount.JWTTokenGenerator("foo", getPrivateKey(rsaPrivateKey))
if err != nil {
t.Fatalf("error making generator: %v", err)
}
badIssuerToken, err := badIssuerGenerator.GenerateToken(serviceaccount.LegacyClaims(*serviceAccount, *rsaSecret))
if err != nil {
t.Fatalf("error generating token: %v", err)

View File

@ -840,15 +840,15 @@
},
{
"ImportPath": "gopkg.in/square/go-jose.v2",
"Rev": "f8f38de21b4dcd69d0413faf231983f5fd6634b1"
"Rev": "89060dee6a84df9a4dae49f676f0c755037834f1"
},
{
"ImportPath": "gopkg.in/square/go-jose.v2/cipher",
"Rev": "f8f38de21b4dcd69d0413faf231983f5fd6634b1"
"Rev": "89060dee6a84df9a4dae49f676f0c755037834f1"
},
{
"ImportPath": "gopkg.in/square/go-jose.v2/json",
"Rev": "f8f38de21b4dcd69d0413faf231983f5fd6634b1"
"Rev": "89060dee6a84df9a4dae49f676f0c755037834f1"
},
{
"ImportPath": "gopkg.in/yaml.v2",

View File

@ -81,7 +81,11 @@ func TestServiceAccountTokenCreate(t *testing.T) {
serviceaccount.NewValidator(aud, serviceaccountgetter.NewGetterFromClient(gcs)),
),
)
masterConfig.ExtraConfig.ServiceAccountIssuer = serviceaccount.JWTTokenGenerator(iss, sk)
tokenGenerator, err := serviceaccount.JWTTokenGenerator(iss, sk)
if err != nil {
t.Fatalf("err: %v", err)
}
masterConfig.ExtraConfig.ServiceAccountIssuer = tokenGenerator
masterConfig.ExtraConfig.ServiceAccountAPIAudiences = aud
masterConfig.ExtraConfig.ServiceAccountMaxExpiration = maxExpirationDuration

View File

@ -435,11 +435,15 @@ func startServiceAccountTestServer(t *testing.T) (*clientset.Clientset, restclie
apiServer.Close()
}
tokenGenerator, err := serviceaccount.JWTTokenGenerator(serviceaccount.LegacyIssuer, serviceAccountKey)
if err != nil {
return rootClientset, clientConfig, stop, err
}
tokenController, err := serviceaccountcontroller.NewTokensController(
informers.Core().V1().ServiceAccounts(),
informers.Core().V1().Secrets(),
rootClientset,
serviceaccountcontroller.TokensControllerOptions{TokenGenerator: serviceaccount.JWTTokenGenerator(serviceaccount.LegacyIssuer, serviceAccountKey)},
serviceaccountcontroller.TokensControllerOptions{TokenGenerator: tokenGenerator},
)
if err != nil {
return rootClientset, clientConfig, stop, err

View File

@ -8,11 +8,12 @@ matrix:
- go: tip
go:
- 1.5
- 1.6
- 1.7
- 1.8
- 1.9
- '1.5.x'
- '1.6.x'
- '1.7.x'
- '1.8.x'
- '1.9.x'
- '1.10.x'
- tip
go_import_path: gopkg.in/square/go-jose.v2

View File

@ -10,6 +10,7 @@ go_library(
"jwe.go",
"jwk.go",
"jws.go",
"opaque.go",
"shared.go",
"signing.go",
"symmetric.go",

View File

@ -84,6 +84,7 @@ standard where possible. The Godoc reference has a list of constants.
RSASSA-PSS | PS256, PS384, PS512
HMAC | HS256, HS384, HS512
ECDSA | ES256, ES384, ES512
Ed25519 | EdDSA
Content encryption | Algorithm identifier(s)
:------------------------- | :------------------------------

View File

@ -104,9 +104,9 @@ func newRSASigner(sigAlg SignatureAlgorithm, privateKey *rsa.PrivateKey) (recipi
return recipientSigInfo{
sigAlg: sigAlg,
publicKey: &JSONWebKey{
Key: &privateKey.PublicKey,
},
publicKey: staticPublicKey(&JSONWebKey{
Key: privateKey.Public(),
}),
signer: &rsaDecrypterSigner{
privateKey: privateKey,
},
@ -123,9 +123,9 @@ func newEd25519Signer(sigAlg SignatureAlgorithm, privateKey ed25519.PrivateKey)
}
return recipientSigInfo{
sigAlg: sigAlg,
publicKey: &JSONWebKey{
publicKey: staticPublicKey(&JSONWebKey{
Key: privateKey.Public(),
},
}),
signer: &edDecrypterSigner{
privateKey: privateKey,
},
@ -168,9 +168,9 @@ func newECDSASigner(sigAlg SignatureAlgorithm, privateKey *ecdsa.PrivateKey) (re
return recipientSigInfo{
sigAlg: sigAlg,
publicKey: &JSONWebKey{
Key: &privateKey.PublicKey,
},
publicKey: staticPublicKey(&JSONWebKey{
Key: privateKey.Public(),
}),
signer: &ecDecrypterSigner{
privateKey: privateKey,
},
@ -466,6 +466,7 @@ func (ctx ecDecrypterSigner) decryptKey(headers rawHeader, recipient *recipientI
return josecipher.KeyUnwrap(block, recipient.encryptedKey)
}
func (ctx edDecrypterSigner) signPayload(payload []byte, alg SignatureAlgorithm) (Signature, error) {
if alg != EdDSA {
return Signature{}, ErrUnsupportedAlgorithm
@ -531,7 +532,7 @@ func (ctx ecDecrypterSigner) signPayload(payload []byte, alg SignatureAlgorithm)
keyBytes++
}
// We serialize the outpus (r and s) into big-endian byte arrays and pad
// We serialize the outputs (r and s) into big-endian byte arrays and pad
// them with zeros on the left to make sure the sizes work out. Both arrays
// must be keyBytes long, and the output must be 2*keyBytes long.
rBytes := r.Bytes()

View File

@ -148,17 +148,10 @@ func (k *JSONWebKey) UnmarshalJSON(data []byte) (err error) {
if err == nil {
*k = JSONWebKey{Key: key, KeyID: raw.Kid, Algorithm: raw.Alg, Use: raw.Use}
}
k.Certificates = make([]*x509.Certificate, len(raw.X5c))
for i, cert := range raw.X5c {
raw, err := base64.StdEncoding.DecodeString(cert)
k.Certificates, err = parseCertificateChain(raw.X5c)
if err != nil {
return err
}
k.Certificates[i], err = x509.ParseCertificate(raw)
if err != nil {
return err
return fmt.Errorf("failed to unmarshal x5c field: %s", err)
}
}
@ -247,13 +240,32 @@ func (k *JSONWebKey) Thumbprint(hash crypto.Hash) ([]byte, error) {
// IsPublic returns true if the JWK represents a public key (not symmetric, not private).
func (k *JSONWebKey) IsPublic() bool {
switch k.Key.(type) {
case *ecdsa.PublicKey, *rsa.PublicKey, *ed25519.PublicKey:
case *ecdsa.PublicKey, *rsa.PublicKey, ed25519.PublicKey:
return true
default:
return false
}
}
// Public creates JSONWebKey with corresponding publik key if JWK represents asymmetric private key.
func (k *JSONWebKey) Public() JSONWebKey {
if k.IsPublic() {
return *k
}
ret := *k
switch key := k.Key.(type) {
case *ecdsa.PrivateKey:
ret.Key = key.Public()
case *rsa.PrivateKey:
ret.Key = key.Public()
case ed25519.PrivateKey:
ret.Key = key.Public()
default:
return JSONWebKey{} // returning invalid key
}
return ret
}
// Valid checks that the key contains the expected parameters.
func (k *JSONWebKey) Valid() bool {
if k.Key == nil {
@ -276,12 +288,12 @@ func (k *JSONWebKey) Valid() bool {
if key.N == nil || key.E == 0 || key.D == nil || len(key.Primes) < 2 {
return false
}
case *ed25519.PublicKey:
if len(*key) != 32 {
case ed25519.PublicKey:
if len(key) != 32 {
return false
}
case *ed25519.PrivateKey:
if len(*key) != 64 {
case ed25519.PrivateKey:
if len(key) != 64 {
return false
}
default:

View File

@ -52,9 +52,20 @@ type JSONWebSignature struct {
// Signature represents a single signature over the JWS payload and protected header.
type Signature struct {
// Header fields, such as the signature algorithm
// Merged header fields. Contains both protected and unprotected header
// values. Prefer using Protected and Unprotected fields instead of this.
// Values in this header may or may not have been signed and in general
// should not be trusted.
Header Header
// Protected header. Values in this header were signed and
// will be verified as part of the signature verification process.
Protected Header
// Unprotected header. Values in this header were not signed
// and in general should not be trusted.
Unprotected Header
// The actual signature value
Signature []byte
@ -82,7 +93,7 @@ func (sig Signature) mergedHeaders() rawHeader {
}
// Compute data to be signed
func (obj JSONWebSignature) computeAuthData(signature *Signature) []byte {
func (obj JSONWebSignature) computeAuthData(payload []byte, signature *Signature) []byte {
var serializedProtected string
if signature.original != nil && signature.original.Protected != nil {
@ -95,7 +106,7 @@ func (obj JSONWebSignature) computeAuthData(signature *Signature) []byte {
return []byte(fmt.Sprintf("%s.%s",
serializedProtected,
base64.RawURLEncoding.EncodeToString(obj.payload)))
base64.RawURLEncoding.EncodeToString(payload)))
}
// parseSignedFull parses a message in full format.
@ -159,6 +170,20 @@ func (parsed *rawJSONWebSignature) sanitized() (*JSONWebSignature, error) {
return nil, err
}
if signature.header != nil {
signature.Unprotected, err = signature.header.sanitized()
if err != nil {
return nil, err
}
}
if signature.protected != nil {
signature.Protected, err = signature.protected.sanitized()
if err != nil {
return nil, err
}
}
// As per RFC 7515 Section 4.1.3, only public keys are allowed to be embedded.
jwk := signature.Header.JSONWebKey
if jwk != nil && (!jwk.Valid() || !jwk.IsPublic()) {
@ -188,6 +213,20 @@ func (parsed *rawJSONWebSignature) sanitized() (*JSONWebSignature, error) {
return nil, err
}
if obj.Signatures[i].header != nil {
obj.Signatures[i].Unprotected, err = obj.Signatures[i].header.sanitized()
if err != nil {
return nil, err
}
}
if obj.Signatures[i].protected != nil {
obj.Signatures[i].Protected, err = obj.Signatures[i].protected.sanitized()
if err != nil {
return nil, err
}
}
obj.Signatures[i].Signature = sig.Signature.bytes()
// As per RFC 7515 Section 4.1.3, only public keys are allowed to be embedded.

83
vendor/gopkg.in/square/go-jose.v2/opaque.go generated vendored Normal file
View File

@ -0,0 +1,83 @@
/*-
* Copyright 2018 Square Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package jose
// OpaqueSigner is an interface that supports signing payloads with opaque
// private key(s). Private key operations preformed by implementors may, for
// example, occur in a hardware module. An OpaqueSigner may rotate signing keys
// transparently to the user of this interface.
type OpaqueSigner interface {
// Public returns the public key of the current signing key.
Public() *JSONWebKey
// Algs returns a list of supported signing algorithms.
Algs() []SignatureAlgorithm
// SignPayload signs a payload with the current signing key using the given
// algorithm.
SignPayload(payload []byte, alg SignatureAlgorithm) ([]byte, error)
}
type opaqueSigner struct {
signer OpaqueSigner
}
func newOpaqueSigner(alg SignatureAlgorithm, signer OpaqueSigner) (recipientSigInfo, error) {
var algSupported bool
for _, salg := range signer.Algs() {
if alg == salg {
algSupported = true
break
}
}
if !algSupported {
return recipientSigInfo{}, ErrUnsupportedAlgorithm
}
return recipientSigInfo{
sigAlg: alg,
publicKey: signer.Public,
signer: &opaqueSigner{
signer: signer,
},
}, nil
}
func (o *opaqueSigner) signPayload(payload []byte, alg SignatureAlgorithm) (Signature, error) {
out, err := o.signer.SignPayload(payload, alg)
if err != nil {
return Signature{}, err
}
return Signature{
Signature: out,
protected: &rawHeader{},
}, nil
}
// OpaqueVerifier is an interface that supports verifying payloads with opaque
// public key(s). An OpaqueSigner may rotate signing keys transparently to the
// user of this interface.
type OpaqueVerifier interface {
VerifyPayload(payload []byte, signature []byte, alg SignatureAlgorithm) error
}
type opaqueVerifier struct {
verifier OpaqueVerifier
}
func (o *opaqueVerifier) verifyPayload(payload []byte, signature []byte, alg SignatureAlgorithm) error {
return o.verifier.VerifyPayload(payload, signature, alg)
}

View File

@ -18,6 +18,8 @@ package jose
import (
"crypto/elliptic"
"crypto/x509"
"encoding/base64"
"errors"
"fmt"
@ -141,6 +143,7 @@ const (
headerEPK = "epk" // *JSONWebKey
headerIV = "iv" // *byteBuffer
headerTag = "tag" // *byteBuffer
headerX5c = "x5c" // []*x509.Certificate
headerJWK = "jwk" // *JSONWebKey
headerKeyID = "kid" // string
@ -162,11 +165,34 @@ type Header struct {
Algorithm string
Nonce string
// Any headers not recognised above get unmarshaled from JSON in a generic
// manner and placed in this map.
// Unverified certificate chain parsed from x5c header.
certificates []*x509.Certificate
// Any headers not recognised above get unmarshaled
// from JSON in a generic manner and placed in this map.
ExtraHeaders map[HeaderKey]interface{}
}
// Certificates verifies & returns the certificate chain present
// in the x5c header field of a message, if one was present. Returns
// an error if there was no x5c header present or the chain could
// not be validated with the given verify options.
func (h Header) Certificates(opts x509.VerifyOptions) ([][]*x509.Certificate, error) {
if len(h.certificates) == 0 {
return nil, errors.New("square/go-jose: no x5c header present in message")
}
leaf := h.certificates[0]
if opts.Intermediates == nil {
opts.Intermediates = x509.NewCertPool()
for _, intermediate := range h.certificates[1:] {
opts.Intermediates.AddCert(intermediate)
}
}
return leaf.Verify(opts)
}
func (parsed rawHeader) set(k HeaderKey, v interface{}) error {
b, err := json.Marshal(v)
if err != nil {
@ -333,6 +359,18 @@ func (parsed rawHeader) sanitized() (h Header, err error) {
return
}
h.Nonce = s
case headerX5c:
c := []string{}
err = json.Unmarshal(*v, &c)
if err != nil {
err = fmt.Errorf("failed to unmarshal x5c header: %v: %#v", err, string(*v))
return
}
h.certificates, err = parseCertificateChain(c)
if err != nil {
err = fmt.Errorf("failed to unmarshal x5c header: %v: %#v", err, string(*v))
return
}
default:
if h.ExtraHeaders == nil {
h.ExtraHeaders = map[HeaderKey]interface{}{}
@ -349,6 +387,21 @@ func (parsed rawHeader) sanitized() (h Header, err error) {
return
}
func parseCertificateChain(chain []string) ([]*x509.Certificate, error) {
out := make([]*x509.Certificate, len(chain))
for i, cert := range chain {
raw, err := base64.StdEncoding.DecodeString(cert)
if err != nil {
return nil, err
}
out[i], err = x509.ParseCertificate(raw)
if err != nil {
return nil, err
}
}
return out, nil
}
func (dst rawHeader) isSet(k HeaderKey) bool {
dvr := dst[k]
if dvr == nil {

View File

@ -94,10 +94,16 @@ type genericSigner struct {
type recipientSigInfo struct {
sigAlg SignatureAlgorithm
publicKey *JSONWebKey
publicKey func() *JSONWebKey
signer payloadSigner
}
func staticPublicKey(jwk *JSONWebKey) func() *JSONWebKey {
return func() *JSONWebKey {
return jwk
}
}
// NewSigner creates an appropriate signer based on the key type
func NewSigner(sig SigningKey, opts *SignerOptions) (Signer, error) {
return NewMultiSigner([]SigningKey{sig}, opts)
@ -146,9 +152,11 @@ func newVerifier(verificationKey interface{}) (payloadVerifier, error) {
return newVerifier(verificationKey.Key)
case *JSONWebKey:
return newVerifier(verificationKey.Key)
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 {
@ -175,9 +183,11 @@ func makeJWSRecipient(alg SignatureAlgorithm, signingKey interface{}) (recipient
return newJWKSigner(alg, signingKey)
case *JSONWebKey:
return newJWKSigner(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) {
@ -185,16 +195,16 @@ func newJWKSigner(alg SignatureAlgorithm, signingKey JSONWebKey) (recipientSigIn
if err != nil {
return recipientSigInfo{}, err
}
if recipient.publicKey != nil {
if recipient.publicKey != nil && recipient.publicKey() != nil {
// recipient.publicKey is a JWK synthesized for embedding when recipientSigInfo
// was created for the inner key (such as a RSA or ECDSA public key). It contains
// the pub key for embedding, but doesn't have extra params like key id.
publicKey := signingKey
publicKey.Key = recipient.publicKey.Key
recipient.publicKey = &publicKey
publicKey.Key = recipient.publicKey().Key
recipient.publicKey = staticPublicKey(&publicKey)
// This should be impossible, but let's check anyway.
if !recipient.publicKey.IsPublic() {
if !recipient.publicKey().IsPublic() {
return recipientSigInfo{}, errors.New("square/go-jose: public key was unexpectedly not public")
}
}
@ -211,7 +221,7 @@ func (ctx *genericSigner) Sign(payload []byte) (*JSONWebSignature, error) {
headerAlgorithm: string(recipient.sigAlg),
}
if recipient.publicKey != nil {
if recipient.publicKey != nil && recipient.publicKey() != nil {
// We want to embed the JWK or set the kid header, but not both. Having a protected
// header that contains an embedded JWK while also simultaneously containing the kid
// header is confusing, and at least in ACME the two are considered to be mutually
@ -221,9 +231,9 @@ func (ctx *genericSigner) Sign(payload []byte) (*JSONWebSignature, error) {
//
// See https://github.com/square/go-jose/issues/157 for more context.
if ctx.embedJWK {
protected[headerJWK] = recipient.publicKey
protected[headerJWK] = recipient.publicKey()
} else {
protected[headerKeyID] = recipient.publicKey.KeyID
protected[headerKeyID] = recipient.publicKey().KeyID
}
}
@ -280,34 +290,46 @@ func (ctx *genericSigner) Options() SignerOptions {
// payload header. You cannot assume that the key received in a payload is
// trusted.
func (obj JSONWebSignature) Verify(verificationKey interface{}) ([]byte, error) {
verifier, err := newVerifier(verificationKey)
err := obj.DetachedVerify(obj.payload, verificationKey)
if err != nil {
return nil, err
}
return obj.payload, nil
}
// DetachedVerify validates a detached signature on the given payload. In
// 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.
func (obj JSONWebSignature) DetachedVerify(payload []byte, verificationKey interface{}) error {
verifier, err := newVerifier(verificationKey)
if err != nil {
return err
}
if len(obj.Signatures) > 1 {
return nil, errors.New("square/go-jose: too many signatures in payload; expecting only one")
return errors.New("square/go-jose: too many signatures in payload; expecting only one")
}
signature := obj.Signatures[0]
headers := signature.mergedHeaders()
critical, err := headers.getCritical()
if err != nil {
return nil, err
return err
}
if len(critical) > 0 {
// Unsupported crit header
return nil, ErrCryptoFailure
return ErrCryptoFailure
}
input := obj.computeAuthData(&signature)
input := obj.computeAuthData(payload, &signature)
alg := headers.getSignatureAlgorithm()
err = verifier.verifyPayload(input, signature.Signature, alg)
if err == nil {
return obj.payload, nil
return nil
}
return nil, ErrCryptoFailure
return ErrCryptoFailure
}
// VerifyMulti validates (one of the multiple) signatures on the object and
@ -315,10 +337,27 @@ func (obj JSONWebSignature) Verify(verificationKey interface{}) ([]byte, error)
// object and the payload. We return the signature and index to guarantee that
// callers are getting the verified value.
func (obj JSONWebSignature) VerifyMulti(verificationKey interface{}) (int, Signature, []byte, error) {
verifier, err := newVerifier(verificationKey)
idx, sig, err := obj.DetachedVerifyMulti(obj.payload, verificationKey)
if err != nil {
return -1, Signature{}, nil, err
}
return idx, sig, obj.payload, nil
}
// DetachedVerifyMulti validates a detached signature on the given payload with
// a signature/object that has potentially multiple signers. This returns the index
// of the signature that was verified, along with the signature object. We return
// the signature and index to guarantee that callers are getting the verified value.
//
// In most cases, you will probably want to use Verify or VerifyMulti instead.
// 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.
func (obj JSONWebSignature) DetachedVerifyMulti(payload []byte, verificationKey interface{}) (int, Signature, error) {
verifier, err := newVerifier(verificationKey)
if err != nil {
return -1, Signature{}, err
}
for i, signature := range obj.Signatures {
headers := signature.mergedHeaders()
@ -331,13 +370,13 @@ func (obj JSONWebSignature) VerifyMulti(verificationKey interface{}) (int, Signa
continue
}
input := obj.computeAuthData(&signature)
input := obj.computeAuthData(payload, &signature)
alg := headers.getSignatureAlgorithm()
err = verifier.verifyPayload(input, signature.Signature, alg)
if err == nil {
return i, signature, obj.payload, nil
return i, signature, nil
}
}
return -1, Signature{}, nil, ErrCryptoFailure
return -1, Signature{}, ErrCryptoFailure
}