Commit a823c021 authored by Fajran Iman Rusadi's avatar Fajran Iman Rusadi

Update go-jose to v2.1.8

parent 06241eae
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: ...@@ -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: ...@@ -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
......
...@@ -25,7 +25,7 @@ import: ...@@ -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
......
// 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
}
// 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]
}
...@@ -28,7 +28,9 @@ import ( ...@@ -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 { ...@@ -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 { ...@@ -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 ...@@ -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 ...@@ -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 ...@@ -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) { ...@@ -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) { ...@@ -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 ...@@ -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 ...@@ -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 ...@@ -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) ...@@ -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()
......
...@@ -28,7 +28,7 @@ import ( ...@@ -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
......
...@@ -22,12 +22,15 @@ import ( ...@@ -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 { ...@@ -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 { ...@@ -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) ...@@ -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) ...@@ -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) { ...@@ -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 ...@@ -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) { ...@@ -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 ...@@ -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 ...@@ -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 ...@@ -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 ...@@ -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) ...@@ -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) ...@@ -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) ...@@ -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) ...@@ -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 ...@@ -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 ...@@ -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)
}
sanitized, err := headers.sanitized()
if err != nil {
return -1, Header{}, nil, fmt.Errorf("square/go-jose: failed to sanitize header: %v", err)
} }
return index, headers.sanitized(), plaintext, err return index, sanitized, plaintext, err
} }
...@@ -17,10 +17,11 @@ ...@@ -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
...@@ -21,10 +21,11 @@ import ( ...@@ -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")
......
...@@ -18,9 +18,10 @@ package jose ...@@ -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 { ...@@ -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) { ...@@ -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) { ...@@ -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) { ...@@ -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) { ...@@ -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")
} }
} }
......
...@@ -29,6 +29,8 @@ import ( ...@@ -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) { ...@@ -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) { ...@@ -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 {
return err
}
k.Certificates[i], err = x509.ParseCertificate(raw)
if err != nil { if err != nil {
return err return fmt.Errorf("failed to unmarshal x5c field: %s", err)
} }
} }
...@@ -171,6 +180,7 @@ func (s *JSONWebKeySet) Key(kid string) []JSONWebKey { ...@@ -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) { ...@@ -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) { ...@@ -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) { ...@@ -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) { ...@@ -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 { ...@@ -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) { ...@@ -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) { ...@@ -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) { ...@@ -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
......
...@@ -52,9 +52,20 @@ type JSONWebSignature struct { ...@@ -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 { ...@@ -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 { ...@@ -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) { ...@@ -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) { ...@@ -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) { ...@@ -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.
......
/*-
* 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)
}
This diff is collapsed.
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment