Merge pull request #1299 from fajran/update-go-jose

Update go-jose to v2.1.8
This commit is contained in:
Eric Chiang 2018-09-18 15:31:32 -07:00 committed by GitHub
commit 316acbee03
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
18 changed files with 4594 additions and 178 deletions

9
glide.lock generated
View File

@ -1,5 +1,5 @@
hash: c41e5b1fbd68f04f14b6e9b2ebab8bf18b857b3dc3a5754e4adf7c1884981de7 hash: 12d0ad2fc0df4ab221e45c1ba7821708b908033c82741e250cc46dcd445b67eb
updated: 2018-09-05T08:40:39.486766558+02:00 updated: 2018-09-18T23:51:30.787348994+02:00
imports: imports:
- name: github.com/beevik/etree - name: github.com/beevik/etree
version: 4cd0dd976db869f817248477718071a28e978df0 version: 4cd0dd976db869f817248477718071a28e978df0
@ -108,6 +108,9 @@ imports:
subpackages: subpackages:
- bcrypt - bcrypt
- blowfish - blowfish
- ed25519
- ed25519/internal/edwards25519
- pbkdf2
- name: golang.org/x/net - name: golang.org/x/net
version: 5602c733f70afc6dcec6766be0d5034d4c4f14de version: 5602c733f70afc6dcec6766be0d5034d4c4f14de
subpackages: subpackages:
@ -168,7 +171,7 @@ imports:
- name: gopkg.in/ldap.v2 - name: gopkg.in/ldap.v2
version: 0e7db8eb77695b5a952f0e5d78df9ab160050c73 version: 0e7db8eb77695b5a952f0e5d78df9ab160050c73
- name: gopkg.in/square/go-jose.v2 - name: gopkg.in/square/go-jose.v2
version: 8c5257b2f658f86d174ae68c6a592eaf6a9608d9 version: 8254d6c783765f38c8675fae4427a1fe73fbd09d
subpackages: subpackages:
- cipher - cipher
- json - json

View File

@ -25,7 +25,7 @@ import:
# Used for JOSE functionality (JWKs, JWTs, etc.). # Used for JOSE functionality (JWKs, JWTs, etc.).
- package: gopkg.in/square/go-jose.v2 - package: gopkg.in/square/go-jose.v2
version: v2.0.0 version: v2.1.8
subpackages: subpackages:
- cipher - cipher
- json - json

181
vendor/golang.org/x/crypto/ed25519/ed25519.go generated vendored Normal file
View File

@ -0,0 +1,181 @@
// Copyright 2016 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// Package ed25519 implements the Ed25519 signature algorithm. See
// http://ed25519.cr.yp.to/.
//
// These functions are also compatible with the “Ed25519” function defined in
// https://tools.ietf.org/html/draft-irtf-cfrg-eddsa-05.
package ed25519
// This code is a port of the public domain, “ref10” implementation of ed25519
// from SUPERCOP.
import (
"crypto"
cryptorand "crypto/rand"
"crypto/sha512"
"crypto/subtle"
"errors"
"io"
"strconv"
"golang.org/x/crypto/ed25519/internal/edwards25519"
)
const (
// PublicKeySize is the size, in bytes, of public keys as used in this package.
PublicKeySize = 32
// PrivateKeySize is the size, in bytes, of private keys as used in this package.
PrivateKeySize = 64
// SignatureSize is the size, in bytes, of signatures generated and verified by this package.
SignatureSize = 64
)
// PublicKey is the type of Ed25519 public keys.
type PublicKey []byte
// PrivateKey is the type of Ed25519 private keys. It implements crypto.Signer.
type PrivateKey []byte
// Public returns the PublicKey corresponding to priv.
func (priv PrivateKey) Public() crypto.PublicKey {
publicKey := make([]byte, PublicKeySize)
copy(publicKey, priv[32:])
return PublicKey(publicKey)
}
// Sign signs the given message with priv.
// Ed25519 performs two passes over messages to be signed and therefore cannot
// handle pre-hashed messages. Thus opts.HashFunc() must return zero to
// indicate the message hasn't been hashed. This can be achieved by passing
// crypto.Hash(0) as the value for opts.
func (priv PrivateKey) Sign(rand io.Reader, message []byte, opts crypto.SignerOpts) (signature []byte, err error) {
if opts.HashFunc() != crypto.Hash(0) {
return nil, errors.New("ed25519: cannot sign hashed message")
}
return Sign(priv, message), nil
}
// GenerateKey generates a public/private key pair using entropy from rand.
// If rand is nil, crypto/rand.Reader will be used.
func GenerateKey(rand io.Reader) (publicKey PublicKey, privateKey PrivateKey, err error) {
if rand == nil {
rand = cryptorand.Reader
}
privateKey = make([]byte, PrivateKeySize)
publicKey = make([]byte, PublicKeySize)
_, err = io.ReadFull(rand, privateKey[:32])
if err != nil {
return nil, nil, err
}
digest := sha512.Sum512(privateKey[:32])
digest[0] &= 248
digest[31] &= 127
digest[31] |= 64
var A edwards25519.ExtendedGroupElement
var hBytes [32]byte
copy(hBytes[:], digest[:])
edwards25519.GeScalarMultBase(&A, &hBytes)
var publicKeyBytes [32]byte
A.ToBytes(&publicKeyBytes)
copy(privateKey[32:], publicKeyBytes[:])
copy(publicKey, publicKeyBytes[:])
return publicKey, privateKey, nil
}
// Sign signs the message with privateKey and returns a signature. It will
// panic if len(privateKey) is not PrivateKeySize.
func Sign(privateKey PrivateKey, message []byte) []byte {
if l := len(privateKey); l != PrivateKeySize {
panic("ed25519: bad private key length: " + strconv.Itoa(l))
}
h := sha512.New()
h.Write(privateKey[:32])
var digest1, messageDigest, hramDigest [64]byte
var expandedSecretKey [32]byte
h.Sum(digest1[:0])
copy(expandedSecretKey[:], digest1[:])
expandedSecretKey[0] &= 248
expandedSecretKey[31] &= 63
expandedSecretKey[31] |= 64
h.Reset()
h.Write(digest1[32:])
h.Write(message)
h.Sum(messageDigest[:0])
var messageDigestReduced [32]byte
edwards25519.ScReduce(&messageDigestReduced, &messageDigest)
var R edwards25519.ExtendedGroupElement
edwards25519.GeScalarMultBase(&R, &messageDigestReduced)
var encodedR [32]byte
R.ToBytes(&encodedR)
h.Reset()
h.Write(encodedR[:])
h.Write(privateKey[32:])
h.Write(message)
h.Sum(hramDigest[:0])
var hramDigestReduced [32]byte
edwards25519.ScReduce(&hramDigestReduced, &hramDigest)
var s [32]byte
edwards25519.ScMulAdd(&s, &hramDigestReduced, &expandedSecretKey, &messageDigestReduced)
signature := make([]byte, SignatureSize)
copy(signature[:], encodedR[:])
copy(signature[32:], s[:])
return signature
}
// Verify reports whether sig is a valid signature of message by publicKey. It
// will panic if len(publicKey) is not PublicKeySize.
func Verify(publicKey PublicKey, message, sig []byte) bool {
if l := len(publicKey); l != PublicKeySize {
panic("ed25519: bad public key length: " + strconv.Itoa(l))
}
if len(sig) != SignatureSize || sig[63]&224 != 0 {
return false
}
var A edwards25519.ExtendedGroupElement
var publicKeyBytes [32]byte
copy(publicKeyBytes[:], publicKey)
if !A.FromBytes(&publicKeyBytes) {
return false
}
edwards25519.FeNeg(&A.X, &A.X)
edwards25519.FeNeg(&A.T, &A.T)
h := sha512.New()
h.Write(sig[:32])
h.Write(publicKey[:])
h.Write(message)
var digest [64]byte
h.Sum(digest[:0])
var hReduced [32]byte
edwards25519.ScReduce(&hReduced, &digest)
var R edwards25519.ProjectiveGroupElement
var b [32]byte
copy(b[:], sig[32:])
edwards25519.GeDoubleScalarMultVartime(&R, &hReduced, &A, &b)
var checkR [32]byte
R.ToBytes(&checkR)
return subtle.ConstantTimeCompare(sig[:32], checkR[:]) == 1
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

77
vendor/golang.org/x/crypto/pbkdf2/pbkdf2.go generated vendored Normal file
View File

@ -0,0 +1,77 @@
// Copyright 2012 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
/*
Package pbkdf2 implements the key derivation function PBKDF2 as defined in RFC
2898 / PKCS #5 v2.0.
A key derivation function is useful when encrypting data based on a password
or any other not-fully-random data. It uses a pseudorandom function to derive
a secure encryption key based on the password.
While v2.0 of the standard defines only one pseudorandom function to use,
HMAC-SHA1, the drafted v2.1 specification allows use of all five FIPS Approved
Hash Functions SHA-1, SHA-224, SHA-256, SHA-384 and SHA-512 for HMAC. To
choose, you can pass the `New` functions from the different SHA packages to
pbkdf2.Key.
*/
package pbkdf2 // import "golang.org/x/crypto/pbkdf2"
import (
"crypto/hmac"
"hash"
)
// Key derives a key from the password, salt and iteration count, returning a
// []byte of length keylen that can be used as cryptographic key. The key is
// derived based on the method described as PBKDF2 with the HMAC variant using
// the supplied hash function.
//
// For example, to use a HMAC-SHA-1 based PBKDF2 key derivation function, you
// can get a derived key for e.g. AES-256 (which needs a 32-byte key) by
// doing:
//
// dk := pbkdf2.Key([]byte("some password"), salt, 4096, 32, sha1.New)
//
// Remember to get a good random salt. At least 8 bytes is recommended by the
// RFC.
//
// Using a higher iteration count will increase the cost of an exhaustive
// search but will also make derivation proportionally slower.
func Key(password, salt []byte, iter, keyLen int, h func() hash.Hash) []byte {
prf := hmac.New(h, password)
hashLen := prf.Size()
numBlocks := (keyLen + hashLen - 1) / hashLen
var buf [4]byte
dk := make([]byte, 0, numBlocks*hashLen)
U := make([]byte, hashLen)
for block := 1; block <= numBlocks; block++ {
// N.B.: || means concatenation, ^ means XOR
// for each block T_i = U_1 ^ U_2 ^ ... ^ U_iter
// U_1 = PRF(password, salt || uint(i))
prf.Reset()
prf.Write(salt)
buf[0] = byte(block >> 24)
buf[1] = byte(block >> 16)
buf[2] = byte(block >> 8)
buf[3] = byte(block)
prf.Write(buf[:4])
dk = prf.Sum(dk)
T := dk[len(dk)-hashLen:]
copy(U, T)
// U_n = PRF(password, U_(n-1))
for n := 2; n <= iter; n++ {
prf.Reset()
prf.Write(U)
U = U[:0]
U = prf.Sum(U)
for x := range U {
T[x] ^= U[x]
}
}
}
return dk[:keyLen]
}

View File

@ -28,7 +28,9 @@ import (
"fmt" "fmt"
"math/big" "math/big"
"golang.org/x/crypto/ed25519"
"gopkg.in/square/go-jose.v2/cipher" "gopkg.in/square/go-jose.v2/cipher"
"gopkg.in/square/go-jose.v2/json"
) )
// A generic RSA-based encrypter/verifier // A generic RSA-based encrypter/verifier
@ -46,6 +48,10 @@ type ecEncrypterVerifier struct {
publicKey *ecdsa.PublicKey publicKey *ecdsa.PublicKey
} }
type edEncrypterVerifier struct {
publicKey ed25519.PublicKey
}
// A key generator for ECDH-ES // A key generator for ECDH-ES
type ecKeyGenerator struct { type ecKeyGenerator struct {
size int size int
@ -58,6 +64,10 @@ type ecDecrypterSigner struct {
privateKey *ecdsa.PrivateKey privateKey *ecdsa.PrivateKey
} }
type edDecrypterSigner struct {
privateKey ed25519.PrivateKey
}
// newRSARecipient creates recipientKeyInfo based on the given key. // newRSARecipient creates recipientKeyInfo based on the given key.
func newRSARecipient(keyAlg KeyAlgorithm, publicKey *rsa.PublicKey) (recipientKeyInfo, error) { func newRSARecipient(keyAlg KeyAlgorithm, publicKey *rsa.PublicKey) (recipientKeyInfo, error) {
// Verify that key management algorithm is supported by this encrypter // Verify that key management algorithm is supported by this encrypter
@ -94,15 +104,34 @@ func newRSASigner(sigAlg SignatureAlgorithm, privateKey *rsa.PrivateKey) (recipi
return recipientSigInfo{ return recipientSigInfo{
sigAlg: sigAlg, sigAlg: sigAlg,
publicKey: &JSONWebKey{ publicKey: staticPublicKey(&JSONWebKey{
Key: &privateKey.PublicKey, Key: privateKey.Public(),
}, }),
signer: &rsaDecrypterSigner{ signer: &rsaDecrypterSigner{
privateKey: privateKey, privateKey: privateKey,
}, },
}, nil }, nil
} }
func newEd25519Signer(sigAlg SignatureAlgorithm, privateKey ed25519.PrivateKey) (recipientSigInfo, error) {
if sigAlg != EdDSA {
return recipientSigInfo{}, ErrUnsupportedAlgorithm
}
if privateKey == nil {
return recipientSigInfo{}, errors.New("invalid private key")
}
return recipientSigInfo{
sigAlg: sigAlg,
publicKey: staticPublicKey(&JSONWebKey{
Key: privateKey.Public(),
}),
signer: &edDecrypterSigner{
privateKey: privateKey,
},
}, nil
}
// newECDHRecipient creates recipientKeyInfo based on the given key. // newECDHRecipient creates recipientKeyInfo based on the given key.
func newECDHRecipient(keyAlg KeyAlgorithm, publicKey *ecdsa.PublicKey) (recipientKeyInfo, error) { func newECDHRecipient(keyAlg KeyAlgorithm, publicKey *ecdsa.PublicKey) (recipientKeyInfo, error) {
// Verify that key management algorithm is supported by this encrypter // Verify that key management algorithm is supported by this encrypter
@ -139,9 +168,9 @@ func newECDSASigner(sigAlg SignatureAlgorithm, privateKey *ecdsa.PrivateKey) (re
return recipientSigInfo{ return recipientSigInfo{
sigAlg: sigAlg, sigAlg: sigAlg,
publicKey: &JSONWebKey{ publicKey: staticPublicKey(&JSONWebKey{
Key: &privateKey.PublicKey, Key: privateKey.Public(),
}, }),
signer: &ecDecrypterSigner{ signer: &ecDecrypterSigner{
privateKey: privateKey, privateKey: privateKey,
}, },
@ -178,7 +207,7 @@ func (ctx rsaEncrypterVerifier) encrypt(cek []byte, alg KeyAlgorithm) ([]byte, e
// Decrypt the given payload and return the content encryption key. // Decrypt the given payload and return the content encryption key.
func (ctx rsaDecrypterSigner) decryptKey(headers rawHeader, recipient *recipientInfo, generator keyGenerator) ([]byte, error) { func (ctx rsaDecrypterSigner) decryptKey(headers rawHeader, recipient *recipientInfo, generator keyGenerator) ([]byte, error) {
return ctx.decrypt(recipient.encryptedKey, KeyAlgorithm(headers.Alg), generator) return ctx.decrypt(recipient.encryptedKey, headers.getAlgorithm(), generator)
} }
// Decrypt the given payload. Based on the key encryption algorithm, // Decrypt the given payload. Based on the key encryption algorithm,
@ -366,10 +395,15 @@ func (ctx ecKeyGenerator) genKey() ([]byte, rawHeader, error) {
out := josecipher.DeriveECDHES(ctx.algID, []byte{}, []byte{}, priv, ctx.publicKey, ctx.size) out := josecipher.DeriveECDHES(ctx.algID, []byte{}, []byte{}, priv, ctx.publicKey, ctx.size)
b, err := json.Marshal(&JSONWebKey{
Key: &priv.PublicKey,
})
if err != nil {
return nil, nil, err
}
headers := rawHeader{ headers := rawHeader{
Epk: &JSONWebKey{ headerEPK: makeRawMessage(b),
Key: &priv.PublicKey,
},
} }
return out, headers, nil return out, headers, nil
@ -377,11 +411,15 @@ func (ctx ecKeyGenerator) genKey() ([]byte, rawHeader, error) {
// Decrypt the given payload and return the content encryption key. // Decrypt the given payload and return the content encryption key.
func (ctx ecDecrypterSigner) decryptKey(headers rawHeader, recipient *recipientInfo, generator keyGenerator) ([]byte, error) { func (ctx ecDecrypterSigner) decryptKey(headers rawHeader, recipient *recipientInfo, generator keyGenerator) ([]byte, error) {
if headers.Epk == nil { epk, err := headers.getEPK()
if err != nil {
return nil, errors.New("square/go-jose: invalid epk header")
}
if epk == nil {
return nil, errors.New("square/go-jose: missing epk header") return nil, errors.New("square/go-jose: missing epk header")
} }
publicKey, ok := headers.Epk.Key.(*ecdsa.PublicKey) publicKey, ok := epk.Key.(*ecdsa.PublicKey)
if publicKey == nil || !ok { if publicKey == nil || !ok {
return nil, errors.New("square/go-jose: invalid epk header") return nil, errors.New("square/go-jose: invalid epk header")
} }
@ -390,19 +428,26 @@ func (ctx ecDecrypterSigner) decryptKey(headers rawHeader, recipient *recipientI
return nil, errors.New("square/go-jose: invalid public key in epk header") return nil, errors.New("square/go-jose: invalid public key in epk header")
} }
apuData := headers.Apu.bytes() apuData, err := headers.getAPU()
apvData := headers.Apv.bytes() if err != nil {
return nil, errors.New("square/go-jose: invalid apu header")
}
apvData, err := headers.getAPV()
if err != nil {
return nil, errors.New("square/go-jose: invalid apv header")
}
deriveKey := func(algID string, size int) []byte { deriveKey := func(algID string, size int) []byte {
return josecipher.DeriveECDHES(algID, apuData, apvData, ctx.privateKey, publicKey, size) return josecipher.DeriveECDHES(algID, apuData.bytes(), apvData.bytes(), ctx.privateKey, publicKey, size)
} }
var keySize int var keySize int
switch KeyAlgorithm(headers.Alg) { algorithm := headers.getAlgorithm()
switch algorithm {
case ECDH_ES: case ECDH_ES:
// ECDH-ES uses direct key agreement, no key unwrapping necessary. // ECDH-ES uses direct key agreement, no key unwrapping necessary.
return deriveKey(string(headers.Enc), generator.keySize()), nil return deriveKey(string(headers.getEncryption()), generator.keySize()), nil
case ECDH_ES_A128KW: case ECDH_ES_A128KW:
keySize = 16 keySize = 16
case ECDH_ES_A192KW: case ECDH_ES_A192KW:
@ -413,7 +458,7 @@ func (ctx ecDecrypterSigner) decryptKey(headers rawHeader, recipient *recipientI
return nil, ErrUnsupportedAlgorithm return nil, ErrUnsupportedAlgorithm
} }
key := deriveKey(headers.Alg, keySize) key := deriveKey(string(algorithm), keySize)
block, err := aes.NewCipher(key) block, err := aes.NewCipher(key)
if err != nil { if err != nil {
return nil, err return nil, err
@ -422,6 +467,33 @@ func (ctx ecDecrypterSigner) decryptKey(headers rawHeader, recipient *recipientI
return josecipher.KeyUnwrap(block, recipient.encryptedKey) return josecipher.KeyUnwrap(block, recipient.encryptedKey)
} }
func (ctx edDecrypterSigner) signPayload(payload []byte, alg SignatureAlgorithm) (Signature, error) {
if alg != EdDSA {
return Signature{}, ErrUnsupportedAlgorithm
}
sig, err := ctx.privateKey.Sign(randReader, payload, crypto.Hash(0))
if err != nil {
return Signature{}, err
}
return Signature{
Signature: sig,
protected: &rawHeader{},
}, nil
}
func (ctx edEncrypterVerifier) verifyPayload(payload []byte, signature []byte, alg SignatureAlgorithm) error {
if alg != EdDSA {
return ErrUnsupportedAlgorithm
}
ok := ed25519.Verify(ctx.publicKey, payload, signature)
if !ok {
return errors.New("square/go-jose: ed25519 signature failed to verify")
}
return nil
}
// Sign the given payload // Sign the given payload
func (ctx ecDecrypterSigner) signPayload(payload []byte, alg SignatureAlgorithm) (Signature, error) { func (ctx ecDecrypterSigner) signPayload(payload []byte, alg SignatureAlgorithm) (Signature, error) {
var expectedBitSize int var expectedBitSize int
@ -460,7 +532,7 @@ func (ctx ecDecrypterSigner) signPayload(payload []byte, alg SignatureAlgorithm)
keyBytes++ 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 // 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. // must be keyBytes long, and the output must be 2*keyBytes long.
rBytes := r.Bytes() rBytes := r.Bytes()

View File

@ -28,7 +28,7 @@ import (
// size may be at most 1<<16 bytes (64 KiB). // size may be at most 1<<16 bytes (64 KiB).
func DeriveECDHES(alg string, apuData, apvData []byte, priv *ecdsa.PrivateKey, pub *ecdsa.PublicKey, size int) []byte { func DeriveECDHES(alg string, apuData, apvData []byte, priv *ecdsa.PrivateKey, pub *ecdsa.PublicKey, size int) []byte {
if size > 1<<16 { if size > 1<<16 {
panic("ECDH-ES output size too large, must be less than 1<<16") panic("ECDH-ES output size too large, must be less than or equal to 1<<16")
} }
// algId, partyUInfo, partyVInfo inputs must be prefixed with the length // algId, partyUInfo, partyVInfo inputs must be prefixed with the length

View File

@ -22,12 +22,15 @@ import (
"errors" "errors"
"fmt" "fmt"
"reflect" "reflect"
"gopkg.in/square/go-jose.v2/json"
) )
// Encrypter represents an encrypter which produces an encrypted JWE object. // Encrypter represents an encrypter which produces an encrypted JWE object.
type Encrypter interface { type Encrypter interface {
Encrypt(plaintext []byte) (*JSONWebEncryption, error) Encrypt(plaintext []byte) (*JSONWebEncryption, error)
EncryptWithAuthData(plaintext []byte, aad []byte) (*JSONWebEncryption, error) EncryptWithAuthData(plaintext []byte, aad []byte) (*JSONWebEncryption, error)
Options() EncrypterOptions
} }
// A generic content cipher // A generic content cipher
@ -60,6 +63,7 @@ type genericEncrypter struct {
cipher contentCipher cipher contentCipher
recipients []recipientKeyInfo recipients []recipientKeyInfo
keyGenerator keyGenerator keyGenerator keyGenerator
extraHeaders map[HeaderKey]interface{}
} }
type recipientKeyInfo struct { type recipientKeyInfo struct {
@ -71,13 +75,47 @@ type recipientKeyInfo struct {
// EncrypterOptions represents options that can be set on new encrypters. // EncrypterOptions represents options that can be set on new encrypters.
type EncrypterOptions struct { type EncrypterOptions struct {
Compression CompressionAlgorithm 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.
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.
func (eo *EncrypterOptions) WithHeader(k HeaderKey, v interface{}) *EncrypterOptions {
if eo.ExtraHeaders == nil {
eo.ExtraHeaders = map[HeaderKey]interface{}{}
}
eo.ExtraHeaders[k] = v
return eo
}
// WithContentType adds a content type ("cty") header and returns the updated
// EncrypterOptions.
func (eo *EncrypterOptions) WithContentType(contentType ContentType) *EncrypterOptions {
return eo.WithHeader(HeaderContentType, contentType)
}
// WithType adds a type ("typ") header and returns the updated EncrypterOptions.
func (eo *EncrypterOptions) WithType(typ ContentType) *EncrypterOptions {
return eo.WithHeader(HeaderType, typ)
} }
// Recipient represents an algorithm/key to encrypt messages to. // Recipient represents an algorithm/key to encrypt messages to.
//
// PBES2Count and PBES2Salt correspond with the "p2c" and "p2s" headers used
// on the password-based encryption algorithms PBES2-HS256+A128KW,
// PBES2-HS384+A192KW, and PBES2-HS512+A256KW. If they are not provided a safe
// default of 100000 will be used for the count and a 128-bit random salt will
// be generated.
type Recipient struct { type Recipient struct {
Algorithm KeyAlgorithm Algorithm KeyAlgorithm
Key interface{} Key interface{}
KeyID string KeyID string
PBES2Count int
PBES2Salt []byte
} }
// NewEncrypter creates an appropriate encrypter based on the key type // NewEncrypter creates an appropriate encrypter based on the key type
@ -89,6 +127,7 @@ func NewEncrypter(enc ContentEncryption, rcpt Recipient, opts *EncrypterOptions)
} }
if opts != nil { if opts != nil {
encrypter.compressionAlg = opts.Compression encrypter.compressionAlg = opts.Compression
encrypter.extraHeaders = opts.ExtraHeaders
} }
if encrypter.cipher == nil { if encrypter.cipher == nil {
@ -98,9 +137,10 @@ func NewEncrypter(enc ContentEncryption, rcpt Recipient, opts *EncrypterOptions)
var keyID string var keyID string
var rawKey interface{} var rawKey interface{}
switch encryptionKey := rcpt.Key.(type) { switch encryptionKey := rcpt.Key.(type) {
case JSONWebKey:
keyID, rawKey = encryptionKey.KeyID, encryptionKey.Key
case *JSONWebKey: case *JSONWebKey:
keyID = encryptionKey.KeyID keyID, rawKey = encryptionKey.KeyID, encryptionKey.Key
rawKey = encryptionKey.Key
default: default:
rawKey = encryptionKey rawKey = encryptionKey
} }
@ -196,6 +236,14 @@ func (ctx *genericEncrypter) addRecipient(recipient Recipient) (err error) {
recipientInfo.keyID = recipient.KeyID recipientInfo.keyID = recipient.KeyID
} }
switch recipient.Algorithm {
case PBES2_HS256_A128KW, PBES2_HS384_A192KW, PBES2_HS512_A256KW:
if sr, ok := recipientInfo.keyEncrypter.(*symmetricKeyCipher); ok {
sr.p2c = recipient.PBES2Count
sr.p2s = recipient.PBES2Salt
}
}
if err == nil { if err == nil {
ctx.recipients = append(ctx.recipients, recipientInfo) ctx.recipients = append(ctx.recipients, recipientInfo)
} }
@ -210,6 +258,8 @@ func makeJWERecipient(alg KeyAlgorithm, encryptionKey interface{}) (recipientKey
return newECDHRecipient(alg, encryptionKey) return newECDHRecipient(alg, encryptionKey)
case []byte: case []byte:
return newSymmetricRecipient(alg, encryptionKey) return newSymmetricRecipient(alg, encryptionKey)
case string:
return newSymmetricRecipient(alg, []byte(encryptionKey))
case *JSONWebKey: case *JSONWebKey:
recipient, err := makeJWERecipient(alg, encryptionKey.Key) recipient, err := makeJWERecipient(alg, encryptionKey.Key)
recipient.keyID = encryptionKey.KeyID recipient.keyID = encryptionKey.KeyID
@ -234,6 +284,12 @@ func newDecrypter(decryptionKey interface{}) (keyDecrypter, error) {
return &symmetricKeyCipher{ return &symmetricKeyCipher{
key: decryptionKey, key: decryptionKey,
}, nil }, nil
case string:
return &symmetricKeyCipher{
key: []byte(decryptionKey),
}, nil
case JSONWebKey:
return newDecrypter(decryptionKey.Key)
case *JSONWebKey: case *JSONWebKey:
return newDecrypter(decryptionKey.Key) return newDecrypter(decryptionKey.Key)
default: default:
@ -251,9 +307,12 @@ func (ctx *genericEncrypter) EncryptWithAuthData(plaintext, aad []byte) (*JSONWe
obj := &JSONWebEncryption{} obj := &JSONWebEncryption{}
obj.aad = aad obj.aad = aad
obj.protected = &rawHeader{ obj.protected = &rawHeader{}
Enc: ctx.contentAlg, err := obj.protected.set(headerEncryption, ctx.contentAlg)
if err != nil {
return nil, err
} }
obj.recipients = make([]recipientInfo, len(ctx.recipients)) obj.recipients = make([]recipientInfo, len(ctx.recipients))
if len(ctx.recipients) == 0 { if len(ctx.recipients) == 0 {
@ -273,9 +332,16 @@ func (ctx *genericEncrypter) EncryptWithAuthData(plaintext, aad []byte) (*JSONWe
return nil, err return nil, err
} }
recipient.header.Alg = string(info.keyAlg) err = recipient.header.set(headerAlgorithm, info.keyAlg)
if err != nil {
return nil, err
}
if info.keyID != "" { if info.keyID != "" {
recipient.header.Kid = info.keyID err = recipient.header.set(headerKeyID, info.keyID)
if err != nil {
return nil, err
}
} }
obj.recipients[i] = recipient obj.recipients[i] = recipient
} }
@ -293,7 +359,18 @@ func (ctx *genericEncrypter) EncryptWithAuthData(plaintext, aad []byte) (*JSONWe
return nil, err return nil, err
} }
obj.protected.Zip = ctx.compressionAlg err = obj.protected.set(headerCompression, ctx.compressionAlg)
if err != nil {
return nil, err
}
}
for k, v := range ctx.extraHeaders {
b, err := json.Marshal(v)
if err != nil {
return nil, err
}
(*obj.protected)[k] = makeRawMessage(b)
} }
authData := obj.computeAuthData() authData := obj.computeAuthData()
@ -309,6 +386,13 @@ func (ctx *genericEncrypter) EncryptWithAuthData(plaintext, aad []byte) (*JSONWe
return obj, nil return obj, nil
} }
func (ctx *genericEncrypter) Options() EncrypterOptions {
return EncrypterOptions{
Compression: ctx.compressionAlg,
ExtraHeaders: ctx.extraHeaders,
}
}
// Decrypt and validate the object and return the plaintext. Note that this // Decrypt and validate the object and return the plaintext. Note that this
// function does not support multi-recipient, if you desire multi-recipient // function does not support multi-recipient, if you desire multi-recipient
// decryption use DecryptMulti instead. // decryption use DecryptMulti instead.
@ -319,7 +403,12 @@ func (obj JSONWebEncryption) Decrypt(decryptionKey interface{}) ([]byte, error)
return nil, errors.New("square/go-jose: too many recipients in payload; expecting only one") return nil, errors.New("square/go-jose: too many recipients in payload; expecting only one")
} }
if len(headers.Crit) > 0 { critical, err := headers.getCritical()
if err != nil {
return nil, fmt.Errorf("square/go-jose: invalid crit header")
}
if len(critical) > 0 {
return nil, fmt.Errorf("square/go-jose: unsupported crit header") return nil, fmt.Errorf("square/go-jose: unsupported crit header")
} }
@ -328,9 +417,9 @@ func (obj JSONWebEncryption) Decrypt(decryptionKey interface{}) ([]byte, error)
return nil, err return nil, err
} }
cipher := getContentCipher(headers.Enc) cipher := getContentCipher(headers.getEncryption())
if cipher == nil { if cipher == nil {
return nil, fmt.Errorf("square/go-jose: unsupported enc value '%s'", string(headers.Enc)) return nil, fmt.Errorf("square/go-jose: unsupported enc value '%s'", string(headers.getEncryption()))
} }
generator := randomKeyGenerator{ generator := randomKeyGenerator{
@ -360,8 +449,8 @@ func (obj JSONWebEncryption) Decrypt(decryptionKey interface{}) ([]byte, error)
} }
// The "zip" header parameter may only be present in the protected header. // The "zip" header parameter may only be present in the protected header.
if obj.protected.Zip != "" { if comp := obj.protected.getCompression(); comp != "" {
plaintext, err = decompress(obj.protected.Zip, plaintext) plaintext, err = decompress(comp, plaintext)
} }
return plaintext, err return plaintext, err
@ -374,7 +463,12 @@ func (obj JSONWebEncryption) Decrypt(decryptionKey interface{}) ([]byte, error)
func (obj JSONWebEncryption) DecryptMulti(decryptionKey interface{}) (int, Header, []byte, error) { func (obj JSONWebEncryption) DecryptMulti(decryptionKey interface{}) (int, Header, []byte, error) {
globalHeaders := obj.mergedHeaders(nil) globalHeaders := obj.mergedHeaders(nil)
if len(globalHeaders.Crit) > 0 { critical, err := globalHeaders.getCritical()
if err != nil {
return -1, Header{}, nil, fmt.Errorf("square/go-jose: invalid crit header")
}
if len(critical) > 0 {
return -1, Header{}, nil, fmt.Errorf("square/go-jose: unsupported crit header") return -1, Header{}, nil, fmt.Errorf("square/go-jose: unsupported crit header")
} }
@ -383,9 +477,10 @@ func (obj JSONWebEncryption) DecryptMulti(decryptionKey interface{}) (int, Heade
return -1, Header{}, nil, err return -1, Header{}, nil, err
} }
cipher := getContentCipher(globalHeaders.Enc) encryption := globalHeaders.getEncryption()
cipher := getContentCipher(encryption)
if cipher == nil { if cipher == nil {
return -1, Header{}, nil, fmt.Errorf("square/go-jose: unsupported enc value '%s'", string(globalHeaders.Enc)) return -1, Header{}, nil, fmt.Errorf("square/go-jose: unsupported enc value '%s'", string(encryption))
} }
generator := randomKeyGenerator{ generator := randomKeyGenerator{
@ -424,9 +519,14 @@ func (obj JSONWebEncryption) DecryptMulti(decryptionKey interface{}) (int, Heade
} }
// The "zip" header parameter may only be present in the protected header. // The "zip" header parameter may only be present in the protected header.
if obj.protected.Zip != "" { if comp := obj.protected.getCompression(); comp != "" {
plaintext, err = decompress(obj.protected.Zip, plaintext) plaintext, err = decompress(comp, plaintext)
} }
return index, headers.sanitized(), plaintext, err sanitized, err := headers.sanitized()
if err != nil {
return -1, Header{}, nil, fmt.Errorf("square/go-jose: failed to sanitize header: %v", err)
}
return index, sanitized, plaintext, err
} }

View File

@ -17,10 +17,11 @@
/* /*
Package jose aims to provide an implementation of the Javascript Object Signing Package jose aims to provide an implementation of the Javascript Object Signing
and Encryption set of standards. For the moment, it mainly focuses on and Encryption set of standards. It implements encryption and signing based on
encryption and signing based on the JSON Web Encryption and JSON Web Signature the JSON Web Encryption and JSON Web Signature standards, with optional JSON
standards. The library supports both the compact and full serialization Web Token support available in a sub-package. The library supports both the
formats, and has optional support for multiple recipients. compact and full serialization formats, and has optional support for multiple
recipients.
*/ */
package jose package jose

View File

@ -21,10 +21,11 @@ import (
"compress/flate" "compress/flate"
"encoding/base64" "encoding/base64"
"encoding/binary" "encoding/binary"
"encoding/json"
"io" "io"
"math/big" "math/big"
"regexp" "regexp"
"gopkg.in/square/go-jose.v2/json"
) )
var stripWhitespaceRegex = regexp.MustCompile("\\s") var stripWhitespaceRegex = regexp.MustCompile("\\s")

View File

@ -18,9 +18,10 @@ package jose
import ( import (
"encoding/base64" "encoding/base64"
"encoding/json"
"fmt" "fmt"
"strings" "strings"
"gopkg.in/square/go-jose.v2/json"
) )
// rawJSONWebEncryption represents a raw JWE JSON object. Used for parsing/serializing. // rawJSONWebEncryption represents a raw JWE JSON object. Used for parsing/serializing.
@ -85,10 +86,12 @@ func (obj JSONWebEncryption) mergedHeaders(recipient *recipientInfo) rawHeader {
func (obj JSONWebEncryption) computeAuthData() []byte { func (obj JSONWebEncryption) computeAuthData() []byte {
var protected string var protected string
if obj.original != nil { if obj.original != nil && obj.original.Protected != nil {
protected = obj.original.Protected.base64() protected = obj.original.Protected.base64()
} else { } else if obj.protected != nil {
protected = base64.RawURLEncoding.EncodeToString(mustSerializeJSON((obj.protected))) protected = base64.RawURLEncoding.EncodeToString(mustSerializeJSON((obj.protected)))
} else {
protected = ""
} }
output := []byte(protected) output := []byte(protected)
@ -129,9 +132,15 @@ func (parsed *rawJSONWebEncryption) sanitized() (*JSONWebEncryption, error) {
} }
// Check that there is not a nonce in the unprotected headers // Check that there is not a nonce in the unprotected headers
if (parsed.Unprotected != nil && parsed.Unprotected.Nonce != "") || if parsed.Unprotected != nil {
(parsed.Header != nil && parsed.Header.Nonce != "") { if nonce := parsed.Unprotected.getNonce(); nonce != "" {
return nil, ErrUnprotectedNonce return nil, ErrUnprotectedNonce
}
}
if parsed.Header != nil {
if nonce := parsed.Header.getNonce(); nonce != "" {
return nil, ErrUnprotectedNonce
}
} }
if parsed.Protected != nil && len(parsed.Protected.bytes()) > 0 { if parsed.Protected != nil && len(parsed.Protected.bytes()) > 0 {
@ -143,7 +152,12 @@ func (parsed *rawJSONWebEncryption) sanitized() (*JSONWebEncryption, error) {
// Note: this must be called _after_ we parse the protected header, // Note: this must be called _after_ we parse the protected header,
// otherwise fields from the protected header will not get picked up. // otherwise fields from the protected header will not get picked up.
obj.Header = obj.mergedHeaders(nil).sanitized() var err error
mergedHeaders := obj.mergedHeaders(nil)
obj.Header, err = mergedHeaders.sanitized()
if err != nil {
return nil, fmt.Errorf("square/go-jose: cannot sanitize merged headers: %v (%v)", err, mergedHeaders)
}
if len(parsed.Recipients) == 0 { if len(parsed.Recipients) == 0 {
obj.recipients = []recipientInfo{ obj.recipients = []recipientInfo{
@ -161,7 +175,7 @@ func (parsed *rawJSONWebEncryption) sanitized() (*JSONWebEncryption, error) {
} }
// Check that there is not a nonce in the unprotected header // Check that there is not a nonce in the unprotected header
if parsed.Recipients[r].Header != nil && parsed.Recipients[r].Header.Nonce != "" { if parsed.Recipients[r].Header != nil && parsed.Recipients[r].Header.getNonce() != "" {
return nil, ErrUnprotectedNonce return nil, ErrUnprotectedNonce
} }
@ -172,7 +186,7 @@ func (parsed *rawJSONWebEncryption) sanitized() (*JSONWebEncryption, error) {
for _, recipient := range obj.recipients { for _, recipient := range obj.recipients {
headers := obj.mergedHeaders(&recipient) headers := obj.mergedHeaders(&recipient)
if headers.Alg == "" || headers.Enc == "" { if headers.getAlgorithm() == "" || headers.getEncryption() == "" {
return nil, fmt.Errorf("square/go-jose: message is missing alg/enc headers") return nil, fmt.Errorf("square/go-jose: message is missing alg/enc headers")
} }
} }

View File

@ -29,6 +29,8 @@ import (
"reflect" "reflect"
"strings" "strings"
"golang.org/x/crypto/ed25519"
"gopkg.in/square/go-jose.v2/json" "gopkg.in/square/go-jose.v2/json"
) )
@ -73,10 +75,14 @@ func (k JSONWebKey) MarshalJSON() ([]byte, error) {
var err error var err error
switch key := k.Key.(type) { switch key := k.Key.(type) {
case ed25519.PublicKey:
raw = fromEdPublicKey(key)
case *ecdsa.PublicKey: case *ecdsa.PublicKey:
raw, err = fromEcPublicKey(key) raw, err = fromEcPublicKey(key)
case *rsa.PublicKey: case *rsa.PublicKey:
raw = fromRsaPublicKey(key) raw = fromRsaPublicKey(key)
case ed25519.PrivateKey:
raw, err = fromEdPrivateKey(key)
case *ecdsa.PrivateKey: case *ecdsa.PrivateKey:
raw, err = fromEcPrivateKey(key) raw, err = fromEcPrivateKey(key)
case *rsa.PrivateKey: case *rsa.PrivateKey:
@ -126,23 +132,26 @@ func (k *JSONWebKey) UnmarshalJSON(data []byte) (err error) {
} }
case "oct": case "oct":
key, err = raw.symmetricKey() key, err = raw.symmetricKey()
case "OKP":
if raw.Crv == "Ed25519" && raw.X != nil {
if raw.D != nil {
key, err = raw.edPrivateKey()
} else {
key, err = raw.edPublicKey()
}
} else {
err = fmt.Errorf("square/go-jose: unknown curve %s'", raw.Crv)
}
default: default:
err = fmt.Errorf("square/go-jose: unknown json web key type '%s'", raw.Kty) err = fmt.Errorf("square/go-jose: unknown json web key type '%s'", raw.Kty)
} }
if err == nil { if err == nil {
*k = JSONWebKey{Key: key, KeyID: raw.Kid, Algorithm: raw.Alg, Use: raw.Use} *k = JSONWebKey{Key: key, KeyID: raw.Kid, Algorithm: raw.Alg, Use: raw.Use}
}
k.Certificates = make([]*x509.Certificate, len(raw.X5c)) k.Certificates, err = parseCertificateChain(raw.X5c)
for i, cert := range raw.X5c {
raw, err := base64.StdEncoding.DecodeString(cert)
if err != nil { if err != nil {
return err return fmt.Errorf("failed to unmarshal x5c field: %s", err)
}
k.Certificates[i], err = x509.ParseCertificate(raw)
if err != nil {
return err
} }
} }
@ -171,6 +180,7 @@ func (s *JSONWebKeySet) Key(kid string) []JSONWebKey {
const rsaThumbprintTemplate = `{"e":"%s","kty":"RSA","n":"%s"}` const rsaThumbprintTemplate = `{"e":"%s","kty":"RSA","n":"%s"}`
const ecThumbprintTemplate = `{"crv":"%s","kty":"EC","x":"%s","y":"%s"}` const ecThumbprintTemplate = `{"crv":"%s","kty":"EC","x":"%s","y":"%s"}`
const edThumbprintTemplate = `{"crv":"%s","kty":"OKP",x":"%s"}`
func ecThumbprintInput(curve elliptic.Curve, x, y *big.Int) (string, error) { func ecThumbprintInput(curve elliptic.Curve, x, y *big.Int) (string, error) {
coordLength := curveSize(curve) coordLength := curveSize(curve)
@ -179,6 +189,10 @@ func ecThumbprintInput(curve elliptic.Curve, x, y *big.Int) (string, error) {
return "", err return "", err
} }
if len(x.Bytes()) > coordLength || len(y.Bytes()) > coordLength {
return "", errors.New("square/go-jose: invalid elliptic key (too large)")
}
return fmt.Sprintf(ecThumbprintTemplate, crv, return fmt.Sprintf(ecThumbprintTemplate, crv,
newFixedSizeBuffer(x.Bytes(), coordLength).base64(), newFixedSizeBuffer(x.Bytes(), coordLength).base64(),
newFixedSizeBuffer(y.Bytes(), coordLength).base64()), nil newFixedSizeBuffer(y.Bytes(), coordLength).base64()), nil
@ -190,12 +204,23 @@ func rsaThumbprintInput(n *big.Int, e int) (string, error) {
newBuffer(n.Bytes()).base64()), nil newBuffer(n.Bytes()).base64()), nil
} }
func edThumbprintInput(ed ed25519.PublicKey) (string, error) {
crv := "Ed25519"
if len(ed) > 32 {
return "", errors.New("square/go-jose: invalid elliptic key (too large)")
}
return fmt.Sprintf(edThumbprintTemplate, crv,
newFixedSizeBuffer(ed, 32).base64()), nil
}
// Thumbprint computes the JWK Thumbprint of a key using the // Thumbprint computes the JWK Thumbprint of a key using the
// indicated hash algorithm. // indicated hash algorithm.
func (k *JSONWebKey) Thumbprint(hash crypto.Hash) ([]byte, error) { func (k *JSONWebKey) Thumbprint(hash crypto.Hash) ([]byte, error) {
var input string var input string
var err error var err error
switch key := k.Key.(type) { switch key := k.Key.(type) {
case ed25519.PublicKey:
input, err = edThumbprintInput(key)
case *ecdsa.PublicKey: case *ecdsa.PublicKey:
input, err = ecThumbprintInput(key.Curve, key.X, key.Y) input, err = ecThumbprintInput(key.Curve, key.X, key.Y)
case *ecdsa.PrivateKey: case *ecdsa.PrivateKey:
@ -204,6 +229,8 @@ func (k *JSONWebKey) Thumbprint(hash crypto.Hash) ([]byte, error) {
input, err = rsaThumbprintInput(key.N, key.E) input, err = rsaThumbprintInput(key.N, key.E)
case *rsa.PrivateKey: case *rsa.PrivateKey:
input, err = rsaThumbprintInput(key.N, key.E) input, err = rsaThumbprintInput(key.N, key.E)
case ed25519.PrivateKey:
input, err = edThumbprintInput(ed25519.PublicKey(key[0:32]))
default: default:
return nil, fmt.Errorf("square/go-jose: unknown key type '%s'", reflect.TypeOf(key)) return nil, fmt.Errorf("square/go-jose: unknown key type '%s'", reflect.TypeOf(key))
} }
@ -220,13 +247,32 @@ func (k *JSONWebKey) Thumbprint(hash crypto.Hash) ([]byte, error) {
// IsPublic returns true if the JWK represents a public key (not symmetric, not private). // IsPublic returns true if the JWK represents a public key (not symmetric, not private).
func (k *JSONWebKey) IsPublic() bool { func (k *JSONWebKey) IsPublic() bool {
switch k.Key.(type) { switch k.Key.(type) {
case *ecdsa.PublicKey, *rsa.PublicKey: case *ecdsa.PublicKey, *rsa.PublicKey, ed25519.PublicKey:
return true return true
default: default:
return false 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. // Valid checks that the key contains the expected parameters.
func (k *JSONWebKey) Valid() bool { func (k *JSONWebKey) Valid() bool {
if k.Key == nil { if k.Key == nil {
@ -249,6 +295,14 @@ func (k *JSONWebKey) Valid() bool {
if key.N == nil || key.E == 0 || key.D == nil || len(key.Primes) < 2 { if key.N == nil || key.E == 0 || key.D == nil || len(key.Primes) < 2 {
return false return false
} }
case ed25519.PublicKey:
if len(key) != 32 {
return false
}
case ed25519.PrivateKey:
if len(key) != 64 {
return false
}
default: default:
return false return false
} }
@ -266,6 +320,14 @@ func (key rawJSONWebKey) rsaPublicKey() (*rsa.PublicKey, error) {
}, nil }, nil
} }
func fromEdPublicKey(pub ed25519.PublicKey) *rawJSONWebKey {
return &rawJSONWebKey{
Kty: "OKP",
Crv: "Ed25519",
X: newBuffer(pub),
}
}
func fromRsaPublicKey(pub *rsa.PublicKey) *rawJSONWebKey { func fromRsaPublicKey(pub *rsa.PublicKey) *rawJSONWebKey {
return &rawJSONWebKey{ return &rawJSONWebKey{
Kty: "RSA", Kty: "RSA",
@ -334,6 +396,36 @@ func fromEcPublicKey(pub *ecdsa.PublicKey) (*rawJSONWebKey, error) {
return key, nil return key, nil
} }
func (key rawJSONWebKey) edPrivateKey() (ed25519.PrivateKey, error) {
var missing []string
switch {
case key.D == nil:
missing = append(missing, "D")
case key.X == nil:
missing = append(missing, "X")
}
if len(missing) > 0 {
return nil, fmt.Errorf("square/go-jose: invalid Ed25519 private key, missing %s value(s)", strings.Join(missing, ", "))
}
privateKey := make([]byte, ed25519.PrivateKeySize)
copy(privateKey[0:32], key.X.bytes())
copy(privateKey[32:], key.D.bytes())
rv := ed25519.PrivateKey(privateKey)
return rv, nil
}
func (key rawJSONWebKey) edPublicKey() (ed25519.PublicKey, error) {
if key.X == nil {
return nil, fmt.Errorf("square/go-jose: invalid Ed key, missing x value")
}
publicKey := make([]byte, ed25519.PublicKeySize)
copy(publicKey[0:32], key.X.bytes())
rv := ed25519.PublicKey(publicKey)
return rv, nil
}
func (key rawJSONWebKey) rsaPrivateKey() (*rsa.PrivateKey, error) { func (key rawJSONWebKey) rsaPrivateKey() (*rsa.PrivateKey, error) {
var missing []string var missing []string
switch { switch {
@ -379,6 +471,13 @@ func (key rawJSONWebKey) rsaPrivateKey() (*rsa.PrivateKey, error) {
return rv, err return rv, err
} }
func fromEdPrivateKey(ed ed25519.PrivateKey) (*rawJSONWebKey, error) {
raw := fromEdPublicKey(ed25519.PublicKey(ed[0:32]))
raw.D = newBuffer(ed[32:])
return raw, nil
}
func fromRsaPrivateKey(rsa *rsa.PrivateKey) (*rawJSONWebKey, error) { func fromRsaPrivateKey(rsa *rsa.PrivateKey) (*rawJSONWebKey, error) {
if len(rsa.Primes) != 2 { if len(rsa.Primes) != 2 {
return nil, ErrUnsupportedKeyType return nil, ErrUnsupportedKeyType

View File

@ -52,9 +52,20 @@ type JSONWebSignature struct {
// Signature represents a single signature over the JWS payload and protected header. // Signature represents a single signature over the JWS payload and protected header.
type Signature struct { 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 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 // The actual signature value
Signature []byte Signature []byte
@ -82,7 +93,7 @@ func (sig Signature) mergedHeaders() rawHeader {
} }
// Compute data to be signed // Compute data to be signed
func (obj JSONWebSignature) computeAuthData(signature *Signature) []byte { func (obj JSONWebSignature) computeAuthData(payload []byte, signature *Signature) []byte {
var serializedProtected string var serializedProtected string
if signature.original != nil && signature.original.Protected != nil { 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", return []byte(fmt.Sprintf("%s.%s",
serializedProtected, serializedProtected,
base64.RawURLEncoding.EncodeToString(obj.payload))) base64.RawURLEncoding.EncodeToString(payload)))
} }
// parseSignedFull parses a message in full format. // parseSignedFull parses a message in full format.
@ -132,7 +143,7 @@ func (parsed *rawJSONWebSignature) sanitized() (*JSONWebSignature, error) {
} }
// Check that there is not a nonce in the unprotected header // Check that there is not a nonce in the unprotected header
if parsed.Header != nil && parsed.Header.Nonce != "" { if parsed.Header != nil && parsed.Header.getNonce() != "" {
return nil, ErrUnprotectedNonce return nil, ErrUnprotectedNonce
} }
@ -153,7 +164,25 @@ func (parsed *rawJSONWebSignature) sanitized() (*JSONWebSignature, error) {
Signature: parsed.Signature, Signature: parsed.Signature,
} }
signature.Header = signature.mergedHeaders().sanitized() var err error
signature.Header, err = signature.mergedHeaders().sanitized()
if err != nil {
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. // As per RFC 7515 Section 4.1.3, only public keys are allowed to be embedded.
jwk := signature.Header.JSONWebKey jwk := signature.Header.JSONWebKey
@ -174,11 +203,30 @@ func (parsed *rawJSONWebSignature) sanitized() (*JSONWebSignature, error) {
} }
// Check that there is not a nonce in the unprotected header // Check that there is not a nonce in the unprotected header
if sig.Header != nil && sig.Header.Nonce != "" { if sig.Header != nil && sig.Header.getNonce() != "" {
return nil, ErrUnprotectedNonce return nil, ErrUnprotectedNonce
} }
obj.Signatures[i].Header = obj.Signatures[i].mergedHeaders().sanitized() var err error
obj.Signatures[i].Header, err = obj.Signatures[i].mergedHeaders().sanitized()
if err != nil {
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() obj.Signatures[i].Signature = sig.Signature.bytes()
// As per RFC 7515 Section 4.1.3, only public keys are allowed to be embedded. // 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,8 +18,12 @@ package jose
import ( import (
"crypto/elliptic" "crypto/elliptic"
"crypto/x509"
"encoding/base64"
"errors" "errors"
"fmt" "fmt"
"gopkg.in/square/go-jose.v2/json"
) )
// KeyAlgorithm represents a key management algorithm. // KeyAlgorithm represents a key management algorithm.
@ -34,6 +38,9 @@ type ContentEncryption string
// CompressionAlgorithm represents an algorithm used for plaintext compression. // CompressionAlgorithm represents an algorithm used for plaintext compression.
type CompressionAlgorithm string type CompressionAlgorithm string
// ContentType represents type of the contained data.
type ContentType string
var ( var (
// ErrCryptoFailure represents an error in cryptographic primitive. This // ErrCryptoFailure represents an error in cryptographic primitive. This
// occurs when, for example, a message had an invalid authentication tag or // occurs when, for example, a message had an invalid authentication tag or
@ -63,6 +70,7 @@ var (
// Key management algorithms // Key management algorithms
const ( const (
ED25519 = KeyAlgorithm("ED25519")
RSA1_5 = KeyAlgorithm("RSA1_5") // RSA-PKCS1v1.5 RSA1_5 = KeyAlgorithm("RSA1_5") // RSA-PKCS1v1.5
RSA_OAEP = KeyAlgorithm("RSA-OAEP") // RSA-OAEP-SHA1 RSA_OAEP = KeyAlgorithm("RSA-OAEP") // RSA-OAEP-SHA1
RSA_OAEP_256 = KeyAlgorithm("RSA-OAEP-256") // RSA-OAEP-SHA256 RSA_OAEP_256 = KeyAlgorithm("RSA-OAEP-256") // RSA-OAEP-SHA256
@ -84,6 +92,7 @@ const (
// Signature algorithms // Signature algorithms
const ( const (
EdDSA = SignatureAlgorithm("EdDSA")
HS256 = SignatureAlgorithm("HS256") // HMAC using SHA-256 HS256 = SignatureAlgorithm("HS256") // HMAC using SHA-256
HS384 = SignatureAlgorithm("HS384") // HMAC using SHA-384 HS384 = SignatureAlgorithm("HS384") // HMAC using SHA-384
HS512 = SignatureAlgorithm("HS512") // HMAC using SHA-512 HS512 = SignatureAlgorithm("HS512") // HMAC using SHA-512
@ -114,21 +123,44 @@ const (
DEFLATE = CompressionAlgorithm("DEF") // DEFLATE (RFC 1951) DEFLATE = CompressionAlgorithm("DEF") // DEFLATE (RFC 1951)
) )
// A key in the protected header of a JWS object. Use of the Header...
// constants is preferred to enhance type safety.
type HeaderKey string
const (
HeaderType HeaderKey = "typ" // string
HeaderContentType = "cty" // string
// These are set by go-jose and shouldn't need to be set by consumers of the
// library.
headerAlgorithm = "alg" // string
headerEncryption = "enc" // ContentEncryption
headerCompression = "zip" // CompressionAlgorithm
headerCritical = "crit" // []string
headerAPU = "apu" // *byteBuffer
headerAPV = "apv" // *byteBuffer
headerEPK = "epk" // *JSONWebKey
headerIV = "iv" // *byteBuffer
headerTag = "tag" // *byteBuffer
headerX5c = "x5c" // []*x509.Certificate
headerJWK = "jwk" // *JSONWebKey
headerKeyID = "kid" // string
headerNonce = "nonce" // string
headerP2C = "p2c" // *byteBuffer (int)
headerP2S = "p2s" // *byteBuffer ([]byte)
)
// rawHeader represents the JOSE header for JWE/JWS objects (used for parsing). // rawHeader represents the JOSE header for JWE/JWS objects (used for parsing).
type rawHeader struct { //
Alg string `json:"alg,omitempty"` // The decoding of the constituent items is deferred because we want to marshal
Enc ContentEncryption `json:"enc,omitempty"` // some members into particular structs rather than generic maps, but at the
Zip CompressionAlgorithm `json:"zip,omitempty"` // same time we need to receive any extra fields unhandled by this library to
Crit []string `json:"crit,omitempty"` // pass through to consuming code in case it wants to examine them.
Apu *byteBuffer `json:"apu,omitempty"` type rawHeader map[HeaderKey]*json.RawMessage
Apv *byteBuffer `json:"apv,omitempty"`
Epk *JSONWebKey `json:"epk,omitempty"`
Iv *byteBuffer `json:"iv,omitempty"`
Tag *byteBuffer `json:"tag,omitempty"`
Jwk *JSONWebKey `json:"jwk,omitempty"`
Kid string `json:"kid,omitempty"`
Nonce string `json:"nonce,omitempty"`
}
// Header represents the read-only JOSE header for JWE/JWS objects. // Header represents the read-only JOSE header for JWE/JWS objects.
type Header struct { type Header struct {
@ -136,62 +168,295 @@ type Header struct {
JSONWebKey *JSONWebKey JSONWebKey *JSONWebKey
Algorithm string Algorithm string
Nonce string Nonce string
// 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 {
return err
}
parsed[k] = makeRawMessage(b)
return nil
}
// getString gets a string from the raw JSON, defaulting to "".
func (parsed rawHeader) getString(k HeaderKey) string {
v, ok := parsed[k]
if !ok || v == nil {
return ""
}
var s string
err := json.Unmarshal(*v, &s)
if err != nil {
return ""
}
return s
}
// getByteBuffer gets a byte buffer from the raw JSON. Returns (nil, nil) if
// not specified.
func (parsed rawHeader) getByteBuffer(k HeaderKey) (*byteBuffer, error) {
v := parsed[k]
if v == nil {
return nil, nil
}
var bb *byteBuffer
err := json.Unmarshal(*v, &bb)
if err != nil {
return nil, err
}
return bb, nil
}
// getAlgorithm extracts parsed "alg" from the raw JSON as a KeyAlgorithm.
func (parsed rawHeader) getAlgorithm() KeyAlgorithm {
return KeyAlgorithm(parsed.getString(headerAlgorithm))
}
// getSignatureAlgorithm extracts parsed "alg" from the raw JSON as a SignatureAlgorithm.
func (parsed rawHeader) getSignatureAlgorithm() SignatureAlgorithm {
return SignatureAlgorithm(parsed.getString(headerAlgorithm))
}
// getEncryption extracts parsed "enc" from the raw JSON.
func (parsed rawHeader) getEncryption() ContentEncryption {
return ContentEncryption(parsed.getString(headerEncryption))
}
// getCompression extracts parsed "zip" from the raw JSON.
func (parsed rawHeader) getCompression() CompressionAlgorithm {
return CompressionAlgorithm(parsed.getString(headerCompression))
}
func (parsed rawHeader) getNonce() string {
return parsed.getString(headerNonce)
}
// getEPK extracts parsed "epk" from the raw JSON.
func (parsed rawHeader) getEPK() (*JSONWebKey, error) {
v := parsed[headerEPK]
if v == nil {
return nil, nil
}
var epk *JSONWebKey
err := json.Unmarshal(*v, &epk)
if err != nil {
return nil, err
}
return epk, nil
}
// getAPU extracts parsed "apu" from the raw JSON.
func (parsed rawHeader) getAPU() (*byteBuffer, error) {
return parsed.getByteBuffer(headerAPU)
}
// getAPV extracts parsed "apv" from the raw JSON.
func (parsed rawHeader) getAPV() (*byteBuffer, error) {
return parsed.getByteBuffer(headerAPV)
}
// getIV extracts parsed "iv" frpom the raw JSON.
func (parsed rawHeader) getIV() (*byteBuffer, error) {
return parsed.getByteBuffer(headerIV)
}
// getTag extracts parsed "tag" frpom the raw JSON.
func (parsed rawHeader) getTag() (*byteBuffer, error) {
return parsed.getByteBuffer(headerTag)
}
// getJWK extracts parsed "jwk" from the raw JSON.
func (parsed rawHeader) getJWK() (*JSONWebKey, error) {
v := parsed[headerJWK]
if v == nil {
return nil, nil
}
var jwk *JSONWebKey
err := json.Unmarshal(*v, &jwk)
if err != nil {
return nil, err
}
return jwk, nil
}
// getCritical extracts parsed "crit" from the raw JSON. If omitted, it
// returns an empty slice.
func (parsed rawHeader) getCritical() ([]string, error) {
v := parsed[headerCritical]
if v == nil {
return nil, nil
}
var q []string
err := json.Unmarshal(*v, &q)
if err != nil {
return nil, err
}
return q, nil
}
// getS2C extracts parsed "p2c" from the raw JSON.
func (parsed rawHeader) getP2C() (int, error) {
v := parsed[headerP2C]
if v == nil {
return 0, nil
}
var p2c int
err := json.Unmarshal(*v, &p2c)
if err != nil {
return 0, err
}
return p2c, nil
}
// getS2S extracts parsed "p2s" from the raw JSON.
func (parsed rawHeader) getP2S() (*byteBuffer, error) {
return parsed.getByteBuffer(headerP2S)
} }
// 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() Header { func (parsed rawHeader) sanitized() (h Header, err error) {
return Header{ for k, v := range parsed {
KeyID: parsed.Kid, if v == nil {
JSONWebKey: parsed.Jwk, continue
Algorithm: parsed.Alg, }
Nonce: parsed.Nonce, switch k {
case headerJWK:
var jwk *JSONWebKey
err = json.Unmarshal(*v, &jwk)
if err != nil {
err = fmt.Errorf("failed to unmarshal JWK: %v: %#v", err, string(*v))
return
}
h.JSONWebKey = jwk
case headerKeyID:
var s string
err = json.Unmarshal(*v, &s)
if err != nil {
err = fmt.Errorf("failed to unmarshal key ID: %v: %#v", err, string(*v))
return
}
h.KeyID = s
case headerAlgorithm:
var s string
err = json.Unmarshal(*v, &s)
if err != nil {
err = fmt.Errorf("failed to unmarshal algorithm: %v: %#v", err, string(*v))
return
}
h.Algorithm = s
case headerNonce:
var s string
err = json.Unmarshal(*v, &s)
if err != nil {
err = fmt.Errorf("failed to unmarshal nonce: %v: %#v", err, string(*v))
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{}{}
}
var v2 interface{}
err = json.Unmarshal(*v, &v2)
if err != nil {
err = fmt.Errorf("failed to unmarshal value: %v: %#v", err, string(*v))
return
}
h.ExtraHeaders[k] = v2
}
} }
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 {
return false
}
var dv interface{}
err := json.Unmarshal(*dvr, &dv)
if err != nil {
return true
}
if dvStr, ok := dv.(string); ok {
return dvStr != ""
}
return true
} }
// Merge headers from src into dst, giving precedence to headers from l. // Merge headers from src into dst, giving precedence to headers from l.
func (dst *rawHeader) merge(src *rawHeader) { func (dst rawHeader) merge(src *rawHeader) {
if src == nil { if src == nil {
return return
} }
if dst.Alg == "" { for k, v := range *src {
dst.Alg = src.Alg if dst.isSet(k) {
} continue
if dst.Enc == "" { }
dst.Enc = src.Enc
} dst[k] = v
if dst.Zip == "" {
dst.Zip = src.Zip
}
if dst.Crit == nil {
dst.Crit = src.Crit
}
if dst.Crit == nil {
dst.Crit = src.Crit
}
if dst.Apu == nil {
dst.Apu = src.Apu
}
if dst.Apv == nil {
dst.Apv = src.Apv
}
if dst.Epk == nil {
dst.Epk = src.Epk
}
if dst.Iv == nil {
dst.Iv = src.Iv
}
if dst.Tag == nil {
dst.Tag = src.Tag
}
if dst.Kid == "" {
dst.Kid = src.Kid
}
if dst.Jwk == nil {
dst.Jwk = src.Jwk
}
if dst.Nonce == "" {
dst.Nonce = src.Nonce
} }
} }
@ -222,3 +487,8 @@ func curveSize(crv elliptic.Curve) int {
return div + 1 return div + 1
} }
func makeRawMessage(b []byte) *json.RawMessage {
rm := json.RawMessage(b)
return &rm
}

View File

@ -22,6 +22,10 @@ import (
"encoding/base64" "encoding/base64"
"errors" "errors"
"fmt" "fmt"
"golang.org/x/crypto/ed25519"
"gopkg.in/square/go-jose.v2/json"
) )
// NonceSource represents a source of random nonces to go into JWS objects // NonceSource represents a source of random nonces to go into JWS objects
@ -32,6 +36,7 @@ type NonceSource interface {
// Signer represents a signer which takes a payload and produces a signed JWS object. // Signer represents a signer which takes a payload and produces a signed JWS object.
type Signer interface { type Signer interface {
Sign(payload []byte) (*JSONWebSignature, error) Sign(payload []byte) (*JSONWebSignature, error)
Options() SignerOptions
} }
// SigningKey represents an algorithm/key used to sign a message. // SigningKey represents an algorithm/key used to sign a message.
@ -44,6 +49,32 @@ type SigningKey struct {
type SignerOptions struct { type SignerOptions struct {
NonceSource NonceSource NonceSource NonceSource
EmbedJWK bool EmbedJWK bool
// 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.
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.
func (so *SignerOptions) WithHeader(k HeaderKey, v interface{}) *SignerOptions {
if so.ExtraHeaders == nil {
so.ExtraHeaders = map[HeaderKey]interface{}{}
}
so.ExtraHeaders[k] = v
return so
}
// WithContentType adds a content type ("cty") header and returns the updated
// SignerOptions.
func (so *SignerOptions) WithContentType(contentType ContentType) *SignerOptions {
return so.WithHeader(HeaderContentType, contentType)
}
// WithType adds a type ("typ") header and returns the updated SignerOptions.
func (so *SignerOptions) WithType(typ ContentType) *SignerOptions {
return so.WithHeader(HeaderType, typ)
} }
type payloadSigner interface { type payloadSigner interface {
@ -55,17 +86,24 @@ type payloadVerifier interface {
} }
type genericSigner struct { type genericSigner struct {
recipients []recipientSigInfo recipients []recipientSigInfo
nonceSource NonceSource nonceSource NonceSource
embedJWK bool embedJWK bool
extraHeaders map[HeaderKey]interface{}
} }
type recipientSigInfo struct { type recipientSigInfo struct {
sigAlg SignatureAlgorithm sigAlg SignatureAlgorithm
publicKey *JSONWebKey publicKey func() *JSONWebKey
signer payloadSigner signer payloadSigner
} }
func staticPublicKey(jwk *JSONWebKey) func() *JSONWebKey {
return func() *JSONWebKey {
return jwk
}
}
// NewSigner creates an appropriate signer based on the key type // NewSigner creates an appropriate signer based on the key type
func NewSigner(sig SigningKey, opts *SignerOptions) (Signer, error) { func NewSigner(sig SigningKey, opts *SignerOptions) (Signer, error) {
return NewMultiSigner([]SigningKey{sig}, opts) return NewMultiSigner([]SigningKey{sig}, opts)
@ -78,6 +116,7 @@ func NewMultiSigner(sigs []SigningKey, opts *SignerOptions) (Signer, error) {
if opts != nil { if opts != nil {
signer.nonceSource = opts.NonceSource signer.nonceSource = opts.NonceSource
signer.embedJWK = opts.EmbedJWK signer.embedJWK = opts.EmbedJWK
signer.extraHeaders = opts.ExtraHeaders
} }
for _, sig := range sigs { for _, sig := range sigs {
@ -93,6 +132,10 @@ func NewMultiSigner(sigs []SigningKey, opts *SignerOptions) (Signer, error) {
// newVerifier creates a verifier based on the key type // newVerifier creates a verifier based on the key type
func newVerifier(verificationKey interface{}) (payloadVerifier, error) { func newVerifier(verificationKey interface{}) (payloadVerifier, error) {
switch verificationKey := verificationKey.(type) { switch verificationKey := verificationKey.(type) {
case ed25519.PublicKey:
return &edEncrypterVerifier{
publicKey: verificationKey,
}, nil
case *rsa.PublicKey: case *rsa.PublicKey:
return &rsaEncrypterVerifier{ return &rsaEncrypterVerifier{
publicKey: verificationKey, publicKey: verificationKey,
@ -105,11 +148,15 @@ func newVerifier(verificationKey interface{}) (payloadVerifier, error) {
return &symmetricMac{ return &symmetricMac{
key: verificationKey, key: verificationKey,
}, nil }, nil
case JSONWebKey:
return newVerifier(verificationKey.Key)
case *JSONWebKey: case *JSONWebKey:
return newVerifier(verificationKey.Key) 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 { func (ctx *genericSigner) addRecipient(alg SignatureAlgorithm, signingKey interface{}) error {
@ -124,22 +171,44 @@ func (ctx *genericSigner) addRecipient(alg SignatureAlgorithm, signingKey interf
func makeJWSRecipient(alg SignatureAlgorithm, signingKey interface{}) (recipientSigInfo, error) { func makeJWSRecipient(alg SignatureAlgorithm, signingKey interface{}) (recipientSigInfo, error) {
switch signingKey := signingKey.(type) { switch signingKey := signingKey.(type) {
case ed25519.PrivateKey:
return newEd25519Signer(alg, signingKey)
case *rsa.PrivateKey: case *rsa.PrivateKey:
return newRSASigner(alg, signingKey) return newRSASigner(alg, signingKey)
case *ecdsa.PrivateKey: case *ecdsa.PrivateKey:
return newECDSASigner(alg, signingKey) return newECDSASigner(alg, signingKey)
case []byte: case []byte:
return newSymmetricSigner(alg, signingKey) return newSymmetricSigner(alg, signingKey)
case JSONWebKey:
return newJWKSigner(alg, signingKey)
case *JSONWebKey: case *JSONWebKey:
recipient, err := makeJWSRecipient(alg, signingKey.Key) return newJWKSigner(alg, *signingKey)
if err != nil {
return recipientSigInfo{}, err
}
recipient.publicKey.KeyID = signingKey.KeyID
return recipient, nil
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) {
recipient, err := makeJWSRecipient(alg, signingKey.Key)
if err != nil {
return recipientSigInfo{}, err
}
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 = staticPublicKey(&publicKey)
// This should be impossible, but let's check anyway.
if !recipient.publicKey().IsPublic() {
return recipientSigInfo{}, errors.New("square/go-jose: public key was unexpectedly not public")
}
}
return recipient, nil
} }
func (ctx *genericSigner) Sign(payload []byte) (*JSONWebSignature, error) { func (ctx *genericSigner) Sign(payload []byte) (*JSONWebSignature, error) {
@ -148,15 +217,24 @@ func (ctx *genericSigner) Sign(payload []byte) (*JSONWebSignature, error) {
obj.Signatures = make([]Signature, len(ctx.recipients)) obj.Signatures = make([]Signature, len(ctx.recipients))
for i, recipient := range ctx.recipients { for i, recipient := range ctx.recipients {
protected := &rawHeader{ protected := map[HeaderKey]interface{}{
Alg: string(recipient.sigAlg), 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
// exclusive. The fact that both can exist at the same time is a somewhat unfortunate
// result of the JOSE spec. We've decided that this library will only include one or
// the other to avoid this confusion.
//
// See https://github.com/square/go-jose/issues/157 for more context.
if ctx.embedJWK { if ctx.embedJWK {
protected.Jwk = recipient.publicKey protected[headerJWK] = recipient.publicKey()
} else {
protected[headerKeyID] = recipient.publicKey().KeyID
} }
protected.Kid = recipient.publicKey.KeyID
} }
if ctx.nonceSource != nil { if ctx.nonceSource != nil {
@ -164,7 +242,11 @@ func (ctx *genericSigner) Sign(payload []byte) (*JSONWebSignature, error) {
if err != nil { if err != nil {
return nil, fmt.Errorf("square/go-jose: Error generating nonce: %v", err) return nil, fmt.Errorf("square/go-jose: Error generating nonce: %v", err)
} }
protected.Nonce = nonce protected[headerNonce] = nonce
}
for k, v := range ctx.extraHeaders {
protected[k] = v
} }
serializedProtected := mustSerializeJSON(protected) serializedProtected := mustSerializeJSON(protected)
@ -178,13 +260,28 @@ func (ctx *genericSigner) Sign(payload []byte) (*JSONWebSignature, error) {
return nil, err return nil, err
} }
signatureInfo.protected = protected signatureInfo.protected = &rawHeader{}
for k, v := range protected {
b, err := json.Marshal(v)
if err != nil {
return nil, fmt.Errorf("square/go-jose: Error marshalling item %#v: %v", k, err)
}
(*signatureInfo.protected)[k] = makeRawMessage(b)
}
obj.Signatures[i] = signatureInfo obj.Signatures[i] = signatureInfo
} }
return obj, nil return obj, nil
} }
func (ctx *genericSigner) Options() SignerOptions {
return SignerOptions{
NonceSource: ctx.nonceSource,
EmbedJWK: ctx.embedJWK,
ExtraHeaders: ctx.extraHeaders,
}
}
// Verify validates the signature on the object and returns the payload. // 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-sig
// verification use VerifyMulti instead. // verification use VerifyMulti instead.
@ -193,30 +290,53 @@ func (ctx *genericSigner) Sign(payload []byte) (*JSONWebSignature, error) {
// payload header. You cannot assume that the key received in a payload is // payload header. You cannot assume that the key received in a payload is
// trusted. // trusted.
func (obj JSONWebSignature) Verify(verificationKey interface{}) ([]byte, error) { func (obj JSONWebSignature) Verify(verificationKey interface{}) ([]byte, error) {
verifier, err := newVerifier(verificationKey) err := obj.DetachedVerify(obj.payload, verificationKey)
if err != nil { if err != nil {
return nil, err return nil, err
} }
return obj.payload, nil
}
// UnsafePayloadWithoutVerification returns the payload without
// verifying it. The content returned from this function cannot be
// trusted.
func (obj JSONWebSignature) UnsafePayloadWithoutVerification() []byte {
return obj.payload
}
// 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 { 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] signature := obj.Signatures[0]
headers := signature.mergedHeaders() headers := signature.mergedHeaders()
if len(headers.Crit) > 0 { critical, err := headers.getCritical()
if err != nil {
return err
}
if len(critical) > 0 {
// Unsupported crit header // Unsupported crit header
return nil, ErrCryptoFailure return ErrCryptoFailure
} }
input := obj.computeAuthData(&signature) input := obj.computeAuthData(payload, &signature)
alg := SignatureAlgorithm(headers.Alg) alg := headers.getSignatureAlgorithm()
err = verifier.verifyPayload(input, signature.Signature, alg) err = verifier.verifyPayload(input, signature.Signature, alg)
if err == nil { 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 // VerifyMulti validates (one of the multiple) signatures on the object and
@ -224,25 +344,46 @@ func (obj JSONWebSignature) Verify(verificationKey interface{}) ([]byte, error)
// object and the payload. We return the signature and index to guarantee that // object and the payload. We return the signature and index to guarantee that
// callers are getting the verified value. // callers are getting the verified value.
func (obj JSONWebSignature) VerifyMulti(verificationKey interface{}) (int, Signature, []byte, error) { 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 { if err != nil {
return -1, Signature{}, nil, err 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 { for i, signature := range obj.Signatures {
headers := signature.mergedHeaders() headers := signature.mergedHeaders()
if len(headers.Crit) > 0 { critical, err := headers.getCritical()
if err != nil {
continue
}
if len(critical) > 0 {
// Unsupported crit header // Unsupported crit header
continue continue
} }
input := obj.computeAuthData(&signature) input := obj.computeAuthData(payload, &signature)
alg := SignatureAlgorithm(headers.Alg) alg := headers.getSignatureAlgorithm()
err := verifier.verifyPayload(input, signature.Signature, alg) err = verifier.verifyPayload(input, signature.Signature, alg)
if err == nil { if err == nil {
return i, signature, obj.payload, nil return i, signature, nil
} }
} }
return -1, Signature{}, nil, ErrCryptoFailure return -1, Signature{}, ErrCryptoFailure
} }

View File

@ -17,6 +17,7 @@
package jose package jose
import ( import (
"bytes"
"crypto/aes" "crypto/aes"
"crypto/cipher" "crypto/cipher"
"crypto/hmac" "crypto/hmac"
@ -25,18 +26,34 @@ import (
"crypto/sha512" "crypto/sha512"
"crypto/subtle" "crypto/subtle"
"errors" "errors"
"fmt"
"hash" "hash"
"io" "io"
"golang.org/x/crypto/pbkdf2"
"gopkg.in/square/go-jose.v2/cipher" "gopkg.in/square/go-jose.v2/cipher"
) )
// Random reader (stubbed out in tests) // Random reader (stubbed out in tests)
var randReader = rand.Reader var randReader = rand.Reader
const (
// RFC7518 recommends a minimum of 1,000 iterations:
// 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
// Default salt size: 128 bits
defaultP2SSize = 16
)
// Dummy key cipher for shared symmetric key mode // Dummy key cipher for shared symmetric key mode
type symmetricKeyCipher struct { type symmetricKeyCipher struct {
key []byte // Pre-shared content-encryption key key []byte // Pre-shared content-encryption key
p2c int // PBES2 Count
p2s []byte // PBES2 Salt Input
} }
// Signer/verifier for MAC modes // Signer/verifier for MAC modes
@ -113,10 +130,37 @@ func getContentCipher(alg ContentEncryption) contentCipher {
} }
} }
// getPbkdf2Params returns the key length and hash function used in
// pbkdf2.Key.
func getPbkdf2Params(alg KeyAlgorithm) (int, func() hash.Hash) {
switch alg {
case PBES2_HS256_A128KW:
return 16, sha256.New
case PBES2_HS384_A192KW:
return 24, sha512.New384
case PBES2_HS512_A256KW:
return 32, sha512.New
default:
panic("invalid algorithm")
}
}
// getRandomSalt generates a new salt of the given size.
func getRandomSalt(size int) ([]byte, error) {
salt := make([]byte, size)
_, err := io.ReadFull(randReader, salt)
if err != nil {
return nil, err
}
return salt, nil
}
// newSymmetricRecipient creates a JWE encrypter based on AES-GCM key wrap. // newSymmetricRecipient creates a JWE encrypter based on AES-GCM key wrap.
func newSymmetricRecipient(keyAlg KeyAlgorithm, key []byte) (recipientKeyInfo, error) { func newSymmetricRecipient(keyAlg KeyAlgorithm, key []byte) (recipientKeyInfo, error) {
switch keyAlg { switch keyAlg {
case DIRECT, A128GCMKW, A192GCMKW, A256GCMKW, A128KW, A192KW, A256KW: case DIRECT, A128GCMKW, A192GCMKW, A256GCMKW, A128KW, A192KW, A256KW:
case PBES2_HS256_A128KW, PBES2_HS384_A192KW, PBES2_HS512_A256KW:
default: default:
return recipientKeyInfo{}, ErrUnsupportedAlgorithm return recipientKeyInfo{}, ErrUnsupportedAlgorithm
} }
@ -211,6 +255,10 @@ func (ctx aeadContentCipher) decrypt(key, aad []byte, parts *aeadParts) ([]byte,
return nil, err return nil, err
} }
if len(parts.iv) != aead.NonceSize() || len(parts.tag) < ctx.authtagBytes {
return nil, ErrCryptoFailure
}
return aead.Open(nil, parts.iv, append(parts.ciphertext, parts.tag...), aad) return aead.Open(nil, parts.iv, append(parts.ciphertext, parts.tag...), aad)
} }
@ -229,11 +277,12 @@ func (ctx *symmetricKeyCipher) encryptKey(cek []byte, alg KeyAlgorithm) (recipie
return recipientInfo{}, err return recipientInfo{}, err
} }
header := &rawHeader{}
header.set(headerIV, newBuffer(parts.iv))
header.set(headerTag, newBuffer(parts.tag))
return recipientInfo{ return recipientInfo{
header: &rawHeader{ header: header,
Iv: newBuffer(parts.iv),
Tag: newBuffer(parts.tag),
},
encryptedKey: parts.ciphertext, encryptedKey: parts.ciphertext,
}, nil }, nil
case A128KW, A192KW, A256KW: case A128KW, A192KW, A256KW:
@ -251,6 +300,45 @@ func (ctx *symmetricKeyCipher) encryptKey(cek []byte, alg KeyAlgorithm) (recipie
encryptedKey: jek, encryptedKey: jek,
header: &rawHeader{}, header: &rawHeader{},
}, nil }, nil
case PBES2_HS256_A128KW, PBES2_HS384_A192KW, PBES2_HS512_A256KW:
if len(ctx.p2s) == 0 {
salt, err := getRandomSalt(defaultP2SSize)
if err != nil {
return recipientInfo{}, err
}
ctx.p2s = salt
}
if ctx.p2c <= 0 {
ctx.p2c = defaultP2C
}
// salt is UTF8(Alg) || 0x00 || Salt Input
salt := bytes.Join([][]byte{[]byte(alg), ctx.p2s}, []byte{0x00})
// derive key
keyLen, h := getPbkdf2Params(alg)
key := pbkdf2.Key(ctx.key, salt, ctx.p2c, keyLen, h)
// use AES cipher with derived key
block, err := aes.NewCipher(key)
if err != nil {
return recipientInfo{}, err
}
jek, err := josecipher.KeyWrap(block, cek)
if err != nil {
return recipientInfo{}, err
}
header := &rawHeader{}
header.set(headerP2C, ctx.p2c)
header.set(headerP2S, newBuffer(ctx.p2s))
return recipientInfo{
encryptedKey: jek,
header: header,
}, nil
} }
return recipientInfo{}, ErrUnsupportedAlgorithm return recipientInfo{}, ErrUnsupportedAlgorithm
@ -258,7 +346,7 @@ func (ctx *symmetricKeyCipher) encryptKey(cek []byte, alg KeyAlgorithm) (recipie
// Decrypt the content encryption key. // Decrypt the content encryption key.
func (ctx *symmetricKeyCipher) decryptKey(headers rawHeader, recipient *recipientInfo, generator keyGenerator) ([]byte, error) { func (ctx *symmetricKeyCipher) decryptKey(headers rawHeader, recipient *recipientInfo, generator keyGenerator) ([]byte, error) {
switch KeyAlgorithm(headers.Alg) { switch headers.getAlgorithm() {
case DIRECT: case DIRECT:
cek := make([]byte, len(ctx.key)) cek := make([]byte, len(ctx.key))
copy(cek, ctx.key) copy(cek, ctx.key)
@ -266,10 +354,19 @@ func (ctx *symmetricKeyCipher) decryptKey(headers rawHeader, recipient *recipien
case A128GCMKW, A192GCMKW, A256GCMKW: case A128GCMKW, A192GCMKW, A256GCMKW:
aead := newAESGCM(len(ctx.key)) aead := newAESGCM(len(ctx.key))
iv, err := headers.getIV()
if err != nil {
return nil, fmt.Errorf("square/go-jose: invalid IV: %v", err)
}
tag, err := headers.getTag()
if err != nil {
return nil, fmt.Errorf("square/go-jose: invalid tag: %v", err)
}
parts := &aeadParts{ parts := &aeadParts{
iv: headers.Iv.bytes(), iv: iv.bytes(),
ciphertext: recipient.encryptedKey, ciphertext: recipient.encryptedKey,
tag: headers.Tag.bytes(), tag: tag.bytes(),
} }
cek, err := aead.decrypt(ctx.key, []byte{}, parts) cek, err := aead.decrypt(ctx.key, []byte{}, parts)
@ -284,6 +381,42 @@ func (ctx *symmetricKeyCipher) decryptKey(headers rawHeader, recipient *recipien
return nil, err return nil, err
} }
cek, err := josecipher.KeyUnwrap(block, recipient.encryptedKey)
if err != nil {
return nil, err
}
return cek, nil
case PBES2_HS256_A128KW, PBES2_HS384_A192KW, PBES2_HS512_A256KW:
p2s, err := headers.getP2S()
if err != nil {
return nil, fmt.Errorf("square/go-jose: invalid P2S: %v", err)
}
if p2s == nil || len(p2s.data) == 0 {
return nil, fmt.Errorf("square/go-jose: invalid P2S: must be present")
}
p2c, err := headers.getP2C()
if err != nil {
return nil, fmt.Errorf("square/go-jose: invalid P2C: %v", err)
}
if p2c <= 0 {
return nil, fmt.Errorf("square/go-jose: invalid P2C: must be a positive integer")
}
// salt is UTF8(Alg) || 0x00 || Salt Input
alg := headers.getAlgorithm()
salt := bytes.Join([][]byte{[]byte(alg), p2s.bytes()}, []byte{0x00})
// derive key
keyLen, h := getPbkdf2Params(alg)
key := pbkdf2.Key(ctx.key, salt, p2c, keyLen, h)
// use AES cipher with derived key
block, err := aes.NewCipher(key)
if err != nil {
return nil, err
}
cek, err := josecipher.KeyUnwrap(block, recipient.encryptedKey) cek, err := josecipher.KeyUnwrap(block, recipient.encryptedKey)
if err != nil { if err != nil {
return nil, err return nil, err