a685e3fc98
Vndr has a simpler configuration and allows pointing to forked packages. Additionally other docker projects are now using vndr making vendoring in distribution more consistent. Updates letsencrypt to use fork. No longer uses sub-vendored packages. Signed-off-by: Derek McGowan <derek@mcgstyle.net> (github: dmcgowan)
499 lines
13 KiB
Go
499 lines
13 KiB
Go
/*-
|
|
* Copyright 2014 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
|
|
|
|
import (
|
|
"crypto"
|
|
"crypto/aes"
|
|
"crypto/ecdsa"
|
|
"crypto/rand"
|
|
"crypto/rsa"
|
|
"crypto/sha1"
|
|
"crypto/sha256"
|
|
"errors"
|
|
"fmt"
|
|
"math/big"
|
|
|
|
"gopkg.in/square/go-jose.v1/cipher"
|
|
)
|
|
|
|
// A generic RSA-based encrypter/verifier
|
|
type rsaEncrypterVerifier struct {
|
|
publicKey *rsa.PublicKey
|
|
}
|
|
|
|
// A generic RSA-based decrypter/signer
|
|
type rsaDecrypterSigner struct {
|
|
privateKey *rsa.PrivateKey
|
|
}
|
|
|
|
// A generic EC-based encrypter/verifier
|
|
type ecEncrypterVerifier struct {
|
|
publicKey *ecdsa.PublicKey
|
|
}
|
|
|
|
// A key generator for ECDH-ES
|
|
type ecKeyGenerator struct {
|
|
size int
|
|
algID string
|
|
publicKey *ecdsa.PublicKey
|
|
}
|
|
|
|
// A generic EC-based decrypter/signer
|
|
type ecDecrypterSigner struct {
|
|
privateKey *ecdsa.PrivateKey
|
|
}
|
|
|
|
// newRSARecipient creates recipientKeyInfo based on the given key.
|
|
func newRSARecipient(keyAlg KeyAlgorithm, publicKey *rsa.PublicKey) (recipientKeyInfo, error) {
|
|
// Verify that key management algorithm is supported by this encrypter
|
|
switch keyAlg {
|
|
case RSA1_5, RSA_OAEP, RSA_OAEP_256:
|
|
default:
|
|
return recipientKeyInfo{}, ErrUnsupportedAlgorithm
|
|
}
|
|
|
|
return recipientKeyInfo{
|
|
keyAlg: keyAlg,
|
|
keyEncrypter: &rsaEncrypterVerifier{
|
|
publicKey: publicKey,
|
|
},
|
|
}, nil
|
|
}
|
|
|
|
// newRSASigner creates a recipientSigInfo based on the given key.
|
|
func newRSASigner(sigAlg SignatureAlgorithm, privateKey *rsa.PrivateKey) (recipientSigInfo, error) {
|
|
// Verify that key management algorithm is supported by this encrypter
|
|
switch sigAlg {
|
|
case RS256, RS384, RS512, PS256, PS384, PS512:
|
|
default:
|
|
return recipientSigInfo{}, ErrUnsupportedAlgorithm
|
|
}
|
|
|
|
return recipientSigInfo{
|
|
sigAlg: sigAlg,
|
|
publicKey: &JsonWebKey{
|
|
Key: &privateKey.PublicKey,
|
|
},
|
|
signer: &rsaDecrypterSigner{
|
|
privateKey: privateKey,
|
|
},
|
|
}, nil
|
|
}
|
|
|
|
// newECDHRecipient creates recipientKeyInfo based on the given key.
|
|
func newECDHRecipient(keyAlg KeyAlgorithm, publicKey *ecdsa.PublicKey) (recipientKeyInfo, error) {
|
|
// Verify that key management algorithm is supported by this encrypter
|
|
switch keyAlg {
|
|
case ECDH_ES, ECDH_ES_A128KW, ECDH_ES_A192KW, ECDH_ES_A256KW:
|
|
default:
|
|
return recipientKeyInfo{}, ErrUnsupportedAlgorithm
|
|
}
|
|
|
|
return recipientKeyInfo{
|
|
keyAlg: keyAlg,
|
|
keyEncrypter: &ecEncrypterVerifier{
|
|
publicKey: publicKey,
|
|
},
|
|
}, nil
|
|
}
|
|
|
|
// newECDSASigner creates a recipientSigInfo based on the given key.
|
|
func newECDSASigner(sigAlg SignatureAlgorithm, privateKey *ecdsa.PrivateKey) (recipientSigInfo, error) {
|
|
// Verify that key management algorithm is supported by this encrypter
|
|
switch sigAlg {
|
|
case ES256, ES384, ES512:
|
|
default:
|
|
return recipientSigInfo{}, ErrUnsupportedAlgorithm
|
|
}
|
|
|
|
return recipientSigInfo{
|
|
sigAlg: sigAlg,
|
|
publicKey: &JsonWebKey{
|
|
Key: &privateKey.PublicKey,
|
|
},
|
|
signer: &ecDecrypterSigner{
|
|
privateKey: privateKey,
|
|
},
|
|
}, nil
|
|
}
|
|
|
|
// Encrypt the given payload and update the object.
|
|
func (ctx rsaEncrypterVerifier) encryptKey(cek []byte, alg KeyAlgorithm) (recipientInfo, error) {
|
|
encryptedKey, err := ctx.encrypt(cek, alg)
|
|
if err != nil {
|
|
return recipientInfo{}, err
|
|
}
|
|
|
|
return recipientInfo{
|
|
encryptedKey: encryptedKey,
|
|
header: &rawHeader{},
|
|
}, nil
|
|
}
|
|
|
|
// Encrypt the given payload. Based on the key encryption algorithm,
|
|
// this will either use RSA-PKCS1v1.5 or RSA-OAEP (with SHA-1 or SHA-256).
|
|
func (ctx rsaEncrypterVerifier) encrypt(cek []byte, alg KeyAlgorithm) ([]byte, error) {
|
|
switch alg {
|
|
case RSA1_5:
|
|
return rsa.EncryptPKCS1v15(randReader, ctx.publicKey, cek)
|
|
case RSA_OAEP:
|
|
return rsa.EncryptOAEP(sha1.New(), randReader, ctx.publicKey, cek, []byte{})
|
|
case RSA_OAEP_256:
|
|
return rsa.EncryptOAEP(sha256.New(), randReader, ctx.publicKey, cek, []byte{})
|
|
}
|
|
|
|
return nil, ErrUnsupportedAlgorithm
|
|
}
|
|
|
|
// Decrypt the given payload and return the content encryption key.
|
|
func (ctx rsaDecrypterSigner) decryptKey(headers rawHeader, recipient *recipientInfo, generator keyGenerator) ([]byte, error) {
|
|
return ctx.decrypt(recipient.encryptedKey, KeyAlgorithm(headers.Alg), generator)
|
|
}
|
|
|
|
// Decrypt the given payload. Based on the key encryption algorithm,
|
|
// this will either use RSA-PKCS1v1.5 or RSA-OAEP (with SHA-1 or SHA-256).
|
|
func (ctx rsaDecrypterSigner) decrypt(jek []byte, alg KeyAlgorithm, generator keyGenerator) ([]byte, error) {
|
|
// Note: The random reader on decrypt operations is only used for blinding,
|
|
// so stubbing is meanlingless (hence the direct use of rand.Reader).
|
|
switch alg {
|
|
case RSA1_5:
|
|
defer func() {
|
|
// DecryptPKCS1v15SessionKey sometimes panics on an invalid payload
|
|
// because of an index out of bounds error, which we want to ignore.
|
|
// This has been fixed in Go 1.3.1 (released 2014/08/13), the recover()
|
|
// only exists for preventing crashes with unpatched versions.
|
|
// See: https://groups.google.com/forum/#!topic/golang-dev/7ihX6Y6kx9k
|
|
// See: https://code.google.com/p/go/source/detail?r=58ee390ff31602edb66af41ed10901ec95904d33
|
|
_ = recover()
|
|
}()
|
|
|
|
// Perform some input validation.
|
|
keyBytes := ctx.privateKey.PublicKey.N.BitLen() / 8
|
|
if keyBytes != len(jek) {
|
|
// Input size is incorrect, the encrypted payload should always match
|
|
// the size of the public modulus (e.g. using a 2048 bit key will
|
|
// produce 256 bytes of output). Reject this since it's invalid input.
|
|
return nil, ErrCryptoFailure
|
|
}
|
|
|
|
cek, _, err := generator.genKey()
|
|
if err != nil {
|
|
return nil, ErrCryptoFailure
|
|
}
|
|
|
|
// When decrypting an RSA-PKCS1v1.5 payload, we must take precautions to
|
|
// prevent chosen-ciphertext attacks as described in RFC 3218, "Preventing
|
|
// the Million Message Attack on Cryptographic Message Syntax". We are
|
|
// therefore deliberatly ignoring errors here.
|
|
_ = rsa.DecryptPKCS1v15SessionKey(rand.Reader, ctx.privateKey, jek, cek)
|
|
|
|
return cek, nil
|
|
case RSA_OAEP:
|
|
// Use rand.Reader for RSA blinding
|
|
return rsa.DecryptOAEP(sha1.New(), rand.Reader, ctx.privateKey, jek, []byte{})
|
|
case RSA_OAEP_256:
|
|
// Use rand.Reader for RSA blinding
|
|
return rsa.DecryptOAEP(sha256.New(), rand.Reader, ctx.privateKey, jek, []byte{})
|
|
}
|
|
|
|
return nil, ErrUnsupportedAlgorithm
|
|
}
|
|
|
|
// Sign the given payload
|
|
func (ctx rsaDecrypterSigner) signPayload(payload []byte, alg SignatureAlgorithm) (Signature, error) {
|
|
var hash crypto.Hash
|
|
|
|
switch alg {
|
|
case RS256, PS256:
|
|
hash = crypto.SHA256
|
|
case RS384, PS384:
|
|
hash = crypto.SHA384
|
|
case RS512, PS512:
|
|
hash = crypto.SHA512
|
|
default:
|
|
return Signature{}, ErrUnsupportedAlgorithm
|
|
}
|
|
|
|
hasher := hash.New()
|
|
|
|
// According to documentation, Write() on hash never fails
|
|
_, _ = hasher.Write(payload)
|
|
hashed := hasher.Sum(nil)
|
|
|
|
var out []byte
|
|
var err error
|
|
|
|
switch alg {
|
|
case RS256, RS384, RS512:
|
|
out, err = rsa.SignPKCS1v15(randReader, ctx.privateKey, hash, hashed)
|
|
case PS256, PS384, PS512:
|
|
out, err = rsa.SignPSS(randReader, ctx.privateKey, hash, hashed, &rsa.PSSOptions{
|
|
SaltLength: rsa.PSSSaltLengthAuto,
|
|
})
|
|
}
|
|
|
|
if err != nil {
|
|
return Signature{}, err
|
|
}
|
|
|
|
return Signature{
|
|
Signature: out,
|
|
protected: &rawHeader{},
|
|
}, nil
|
|
}
|
|
|
|
// Verify the given payload
|
|
func (ctx rsaEncrypterVerifier) verifyPayload(payload []byte, signature []byte, alg SignatureAlgorithm) error {
|
|
var hash crypto.Hash
|
|
|
|
switch alg {
|
|
case RS256, PS256:
|
|
hash = crypto.SHA256
|
|
case RS384, PS384:
|
|
hash = crypto.SHA384
|
|
case RS512, PS512:
|
|
hash = crypto.SHA512
|
|
default:
|
|
return ErrUnsupportedAlgorithm
|
|
}
|
|
|
|
hasher := hash.New()
|
|
|
|
// According to documentation, Write() on hash never fails
|
|
_, _ = hasher.Write(payload)
|
|
hashed := hasher.Sum(nil)
|
|
|
|
switch alg {
|
|
case RS256, RS384, RS512:
|
|
return rsa.VerifyPKCS1v15(ctx.publicKey, hash, hashed, signature)
|
|
case PS256, PS384, PS512:
|
|
return rsa.VerifyPSS(ctx.publicKey, hash, hashed, signature, nil)
|
|
}
|
|
|
|
return ErrUnsupportedAlgorithm
|
|
}
|
|
|
|
// Encrypt the given payload and update the object.
|
|
func (ctx ecEncrypterVerifier) encryptKey(cek []byte, alg KeyAlgorithm) (recipientInfo, error) {
|
|
switch alg {
|
|
case ECDH_ES:
|
|
// ECDH-ES mode doesn't wrap a key, the shared secret is used directly as the key.
|
|
return recipientInfo{
|
|
header: &rawHeader{},
|
|
}, nil
|
|
case ECDH_ES_A128KW, ECDH_ES_A192KW, ECDH_ES_A256KW:
|
|
default:
|
|
return recipientInfo{}, ErrUnsupportedAlgorithm
|
|
}
|
|
|
|
generator := ecKeyGenerator{
|
|
algID: string(alg),
|
|
publicKey: ctx.publicKey,
|
|
}
|
|
|
|
switch alg {
|
|
case ECDH_ES_A128KW:
|
|
generator.size = 16
|
|
case ECDH_ES_A192KW:
|
|
generator.size = 24
|
|
case ECDH_ES_A256KW:
|
|
generator.size = 32
|
|
}
|
|
|
|
kek, header, err := generator.genKey()
|
|
if err != nil {
|
|
return recipientInfo{}, err
|
|
}
|
|
|
|
block, err := aes.NewCipher(kek)
|
|
if err != nil {
|
|
return recipientInfo{}, err
|
|
}
|
|
|
|
jek, err := josecipher.KeyWrap(block, cek)
|
|
if err != nil {
|
|
return recipientInfo{}, err
|
|
}
|
|
|
|
return recipientInfo{
|
|
encryptedKey: jek,
|
|
header: &header,
|
|
}, nil
|
|
}
|
|
|
|
// Get key size for EC key generator
|
|
func (ctx ecKeyGenerator) keySize() int {
|
|
return ctx.size
|
|
}
|
|
|
|
// Get a content encryption key for ECDH-ES
|
|
func (ctx ecKeyGenerator) genKey() ([]byte, rawHeader, error) {
|
|
priv, err := ecdsa.GenerateKey(ctx.publicKey.Curve, randReader)
|
|
if err != nil {
|
|
return nil, rawHeader{}, err
|
|
}
|
|
|
|
out := josecipher.DeriveECDHES(ctx.algID, []byte{}, []byte{}, priv, ctx.publicKey, ctx.size)
|
|
|
|
headers := rawHeader{
|
|
Epk: &JsonWebKey{
|
|
Key: &priv.PublicKey,
|
|
},
|
|
}
|
|
|
|
return out, headers, nil
|
|
}
|
|
|
|
// Decrypt the given payload and return the content encryption key.
|
|
func (ctx ecDecrypterSigner) decryptKey(headers rawHeader, recipient *recipientInfo, generator keyGenerator) ([]byte, error) {
|
|
if headers.Epk == nil {
|
|
return nil, errors.New("square/go-jose: missing epk header")
|
|
}
|
|
|
|
publicKey, ok := headers.Epk.Key.(*ecdsa.PublicKey)
|
|
if publicKey == nil || !ok {
|
|
return nil, errors.New("square/go-jose: invalid epk header")
|
|
}
|
|
|
|
apuData := headers.Apu.bytes()
|
|
apvData := headers.Apv.bytes()
|
|
|
|
deriveKey := func(algID string, size int) []byte {
|
|
return josecipher.DeriveECDHES(algID, apuData, apvData, ctx.privateKey, publicKey, size)
|
|
}
|
|
|
|
var keySize int
|
|
|
|
switch KeyAlgorithm(headers.Alg) {
|
|
case ECDH_ES:
|
|
// ECDH-ES uses direct key agreement, no key unwrapping necessary.
|
|
return deriveKey(string(headers.Enc), generator.keySize()), nil
|
|
case ECDH_ES_A128KW:
|
|
keySize = 16
|
|
case ECDH_ES_A192KW:
|
|
keySize = 24
|
|
case ECDH_ES_A256KW:
|
|
keySize = 32
|
|
default:
|
|
return nil, ErrUnsupportedAlgorithm
|
|
}
|
|
|
|
key := deriveKey(headers.Alg, keySize)
|
|
block, err := aes.NewCipher(key)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return josecipher.KeyUnwrap(block, recipient.encryptedKey)
|
|
}
|
|
|
|
// Sign the given payload
|
|
func (ctx ecDecrypterSigner) signPayload(payload []byte, alg SignatureAlgorithm) (Signature, error) {
|
|
var expectedBitSize int
|
|
var hash crypto.Hash
|
|
|
|
switch alg {
|
|
case ES256:
|
|
expectedBitSize = 256
|
|
hash = crypto.SHA256
|
|
case ES384:
|
|
expectedBitSize = 384
|
|
hash = crypto.SHA384
|
|
case ES512:
|
|
expectedBitSize = 521
|
|
hash = crypto.SHA512
|
|
}
|
|
|
|
curveBits := ctx.privateKey.Curve.Params().BitSize
|
|
if expectedBitSize != curveBits {
|
|
return Signature{}, fmt.Errorf("square/go-jose: expected %d bit key, got %d bits instead", expectedBitSize, curveBits)
|
|
}
|
|
|
|
hasher := hash.New()
|
|
|
|
// According to documentation, Write() on hash never fails
|
|
_, _ = hasher.Write(payload)
|
|
hashed := hasher.Sum(nil)
|
|
|
|
r, s, err := ecdsa.Sign(randReader, ctx.privateKey, hashed)
|
|
if err != nil {
|
|
return Signature{}, err
|
|
}
|
|
|
|
keyBytes := curveBits / 8
|
|
if curveBits%8 > 0 {
|
|
keyBytes += 1
|
|
}
|
|
|
|
// We serialize the outpus (r and s) into big-endian byte arrays and pad
|
|
// them with zeros on the left to make sure the sizes work out. Both arrays
|
|
// must be keyBytes long, and the output must be 2*keyBytes long.
|
|
rBytes := r.Bytes()
|
|
rBytesPadded := make([]byte, keyBytes)
|
|
copy(rBytesPadded[keyBytes-len(rBytes):], rBytes)
|
|
|
|
sBytes := s.Bytes()
|
|
sBytesPadded := make([]byte, keyBytes)
|
|
copy(sBytesPadded[keyBytes-len(sBytes):], sBytes)
|
|
|
|
out := append(rBytesPadded, sBytesPadded...)
|
|
|
|
return Signature{
|
|
Signature: out,
|
|
protected: &rawHeader{},
|
|
}, nil
|
|
}
|
|
|
|
// Verify the given payload
|
|
func (ctx ecEncrypterVerifier) verifyPayload(payload []byte, signature []byte, alg SignatureAlgorithm) error {
|
|
var keySize int
|
|
var hash crypto.Hash
|
|
|
|
switch alg {
|
|
case ES256:
|
|
keySize = 32
|
|
hash = crypto.SHA256
|
|
case ES384:
|
|
keySize = 48
|
|
hash = crypto.SHA384
|
|
case ES512:
|
|
keySize = 66
|
|
hash = crypto.SHA512
|
|
}
|
|
|
|
if len(signature) != 2*keySize {
|
|
return fmt.Errorf("square/go-jose: invalid signature size, have %d bytes, wanted %d", len(signature), 2*keySize)
|
|
}
|
|
|
|
hasher := hash.New()
|
|
|
|
// According to documentation, Write() on hash never fails
|
|
_, _ = hasher.Write(payload)
|
|
hashed := hasher.Sum(nil)
|
|
|
|
r := big.NewInt(0).SetBytes(signature[:keySize])
|
|
s := big.NewInt(0).SetBytes(signature[keySize:])
|
|
|
|
match := ecdsa.Verify(ctx.publicKey, hashed, r, s)
|
|
if !match {
|
|
return errors.New("square/go-jose: ecdsa signature failed to verify")
|
|
}
|
|
|
|
return nil
|
|
}
|