| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181 | <?phpnamespace Aws\Crypto;use GuzzleHttp\Psr7;use GuzzleHttp\Psr7\LimitStream;use Psr\Http\Message\StreamInterface;trait DecryptionTrait{    /**     * Dependency to reverse lookup the openssl_* cipher name from the AESName     * in the MetadataEnvelope.     *     * @param $aesName     *     * @return string     *     * @internal     */    abstract protected function getCipherFromAesName($aesName);    /**     * Dependency to generate a CipherMethod from a set of inputs for loading     * in to an AesDecryptingStream.     *     * @param string $cipherName Name of the cipher to generate for decrypting.     * @param string $iv Base Initialization Vector for the cipher.     * @param int $keySize Size of the encryption key, in bits, that will be     *                     used.     *     * @return Cipher\CipherMethod     *     * @internal     */    abstract protected function buildCipherMethod($cipherName, $iv, $keySize);    /**     * Builds an AesStreamInterface using cipher options loaded from the     * MetadataEnvelope and MaterialsProvider. Can decrypt data from both the     * legacy and V2 encryption client workflows.     *     * @param string $cipherText Plain-text data to be encrypted using the     *                           materials, algorithm, and data provided.     * @param MaterialsProviderInterface $provider A provider to supply and encrypt     *                                             materials used in encryption.     * @param MetadataEnvelope $envelope A storage envelope for encryption     *                                   metadata to be read from.     * @param array $cipherOptions Additional verification options.     *     * @return AesStreamInterface     *     * @throws \InvalidArgumentException Thrown when a value in $cipherOptions     *                                   is not valid.     *     * @internal     */    public function decrypt(        $cipherText,        MaterialsProviderInterface $provider,        MetadataEnvelope $envelope,        array $cipherOptions = []    ) {        $cipherOptions['Iv'] = base64_decode(            $envelope[MetadataEnvelope::IV_HEADER]        );        $cipherOptions['TagLength'] =            $envelope[MetadataEnvelope::CRYPTO_TAG_LENGTH_HEADER] / 8;        $cek = $provider->decryptCek(            base64_decode(                $envelope[MetadataEnvelope::CONTENT_KEY_V2_HEADER]            ),            json_decode(                $envelope[MetadataEnvelope::MATERIALS_DESCRIPTION_HEADER],                true            )        );        $cipherOptions['KeySize'] = strlen($cek) * 8;        $cipherOptions['Cipher'] = $this->getCipherFromAesName(            $envelope[MetadataEnvelope::CONTENT_CRYPTO_SCHEME_HEADER]        );        $decryptionStream = $this->getDecryptingStream(            $cipherText,            $cek,            $cipherOptions        );        unset($cek);        return $decryptionStream;    }    private function getTagFromCiphertextStream(        StreamInterface $cipherText,        $tagLength    ) {        $cipherTextSize = $cipherText->getSize();        if ($cipherTextSize == null || $cipherTextSize <= 0) {            throw new \RuntimeException('Cannot decrypt a stream of unknown'                . ' size.');        }        return (string) new LimitStream(            $cipherText,            $tagLength,            $cipherTextSize - $tagLength        );    }    private function getStrippedCiphertextStream(        StreamInterface $cipherText,        $tagLength    ) {        $cipherTextSize = $cipherText->getSize();        if ($cipherTextSize == null || $cipherTextSize <= 0) {            throw new \RuntimeException('Cannot decrypt a stream of unknown'                . ' size.');        }        return new LimitStream(            $cipherText,            $cipherTextSize - $tagLength,            0        );    }    /**     * Generates a stream that wraps the cipher text with the proper cipher and     * uses the content encryption key (CEK) to decrypt the data when read.     *     * @param string $cipherText Plain-text data to be encrypted using the     *                           materials, algorithm, and data provided.     * @param string $cek A content encryption key for use by the stream for     *                    encrypting the plaintext data.     * @param array $cipherOptions Options for use in determining the cipher to     *                             be used for encrypting data.     *     * @return AesStreamInterface     *     * @internal     */    protected function getDecryptingStream(        $cipherText,        $cek,        $cipherOptions    ) {        $cipherTextStream = Psr7\Utils::streamFor($cipherText);        switch ($cipherOptions['Cipher']) {            case 'gcm':                $cipherOptions['Tag'] = $this->getTagFromCiphertextStream(                        $cipherTextStream,                        $cipherOptions['TagLength']                    );                return new AesGcmDecryptingStream(                    $this->getStrippedCiphertextStream(                        $cipherTextStream,                        $cipherOptions['TagLength']                    ),                    $cek,                    $cipherOptions['Iv'],                    $cipherOptions['Tag'],                    $cipherOptions['Aad'] = isset($cipherOptions['Aad'])                        ? $cipherOptions['Aad']                        : '',                    $cipherOptions['TagLength'] ?: null,                    $cipherOptions['KeySize']                );            default:                $cipherMethod = $this->buildCipherMethod(                    $cipherOptions['Cipher'],                    $cipherOptions['Iv'],                    $cipherOptions['KeySize']                );                return new AesDecryptingStream(                    $cipherTextStream,                    $cek,                    $cipherMethod                );        }    }}
 |