/*- * 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" "github.com/square/go-jose/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 }