/// <summary>Return the raw input stream for the data stream.</summary>
 public override Stream GetInputStream()
 {
     return(EncData.GetInputStream());
 }
        /// <summary>Return the decrypted input stream, using the passed in passphrase.</summary>
        public Stream GetDataStream(char[] passPhrase)
        {
            try
            {
                var keyAlgorithm = _keyData.EncAlgorithm;

                var key = PgpUtilities.MakeKeyFromPassPhrase(
                    keyAlgorithm, _keyData.S2K, passPhrase);


                var secKeyData = _keyData.GetSecKeyData();
                if (secKeyData != null && secKeyData.Length > 0)
                {
                    var keyCipher = CipherUtilities.GetCipher(
                        PgpUtilities.GetSymmetricCipherName(keyAlgorithm) + "/CFB/NoPadding");

                    keyCipher.Init(false,
                                   new ParametersWithIV(key, new byte[keyCipher.GetBlockSize()]));

                    var keyBytes = keyCipher.DoFinal(secKeyData);

                    keyAlgorithm = (SymmetricKeyAlgorithmTag)keyBytes[0];

                    key = ParameterUtilities.CreateKeyParameter(
                        PgpUtilities.GetSymmetricCipherName(keyAlgorithm),
                        keyBytes, 1, keyBytes.Length - 1);
                }


                var c = CreateStreamCipher(keyAlgorithm);

                var iv = new byte[c.GetBlockSize()];

                c.Init(false, new ParametersWithIV(key, iv));

                EncStream = BcpgInputStream.Wrap(new CipherStream(EncData.GetInputStream(), c, null));

                if (EncData is SymmetricEncIntegrityPacket)
                {
                    TruncStream = new TruncatedStream(EncStream);

                    var digestName = PgpUtilities.GetDigestName(HashAlgorithmTag.Sha1);
                    var digest     = DigestUtilities.GetDigest(digestName);

                    EncStream = new DigestStream(TruncStream, digest, null);
                }

                if (Streams.ReadFully(EncStream, iv, 0, iv.Length) < iv.Length)
                {
                    throw new EndOfStreamException("unexpected end of stream.");
                }

                var v1 = EncStream.ReadByte();
                var v2 = EncStream.ReadByte();

                if (v1 < 0 || v2 < 0)
                {
                    throw new EndOfStreamException("unexpected end of stream.");
                }


                // Note: the oracle attack on the "quick check" bytes is not deemed
                // a security risk for PBE (see PgpPublicKeyEncryptedData)

                var repeatCheckPassed =
                    iv[iv.Length - 2] == (byte)v1 &&
                    iv[iv.Length - 1] == (byte)v2;

                // Note: some versions of PGP appear to produce 0 for the extra
                // bytes rather than repeating the two previous bytes
                var zeroesCheckPassed = v1 == 0 && v2 == 0;
                if (!repeatCheckPassed && !zeroesCheckPassed)
                {
                    throw new PgpDataValidationException("quick check failed.");
                }


                return(EncStream);
            }
            catch (PgpException)
            {
                throw;
            }
            catch (Exception e)
            {
                throw new PgpException("Exception creating cipher", e);
            }
        }
        /// <summary>
        /// Gets the data stream.
        /// </summary>
        /// <param name="privKey">The priv key.</param>
        /// <param name="encryptionAlgorithm">The encryption algorithm.</param>
        /// <returns></returns>
        /// <exception cref="Org.BouncyCastle.Bcpg.OpenPgp.PgpException">
        /// exception creating cipher
        /// or
        /// Exception starting decryption
        /// </exception>
        /// <exception cref="System.IO.EndOfStreamException">
        /// unexpected end of stream.
        /// or
        /// unexpected end of stream.
        /// </exception>
        public Stream GetDataStream(IPgpPrivateKey privKey, out SymmetricKeyAlgorithmTag encryptionAlgorithm)
        {
            var plain = this.FetchSymmetricKeyData(privKey);

            encryptionAlgorithm = (SymmetricKeyAlgorithmTag)plain[0];

            IBufferedCipher c2;
            var             cipherName = PgpUtilities.GetSymmetricCipherName(encryptionAlgorithm);
            var             cName      = cipherName;

            try
            {
                if (EncData is SymmetricEncIntegrityPacket)
                {
                    cName += "/CFB/NoPadding";
                }
                else
                {
                    cName += "/OpenPGPCFB/NoPadding";
                }

                c2 = CipherUtilities.GetCipher(cName);
            }
            catch (PgpException)
            {
                throw;
            }
            catch (Exception e)
            {
                throw new PgpException("exception creating cipher", e);
            }

            if (c2 == null)
            {
                return(EncData.GetInputStream());
            }

            try
            {
                var key = ParameterUtilities.CreateKeyParameter(cipherName, plain, 1, plain.Length - 3);
                var iv  = new byte[c2.GetBlockSize()];

                c2.Init(false, new ParametersWithIV(key, iv));

                this.EncStream = BcpgInputStream.Wrap(new CipherStream(EncData.GetInputStream(), c2, null));
                if (this.EncData is SymmetricEncIntegrityPacket)
                {
                    this.TruncStream = new TruncatedStream(this.EncStream);

                    var digest = DigestUtilities.GetDigest(PgpUtilities.GetDigestName(HashAlgorithmTag.Sha1));
                    EncStream = new DigestStream(TruncStream, digest, null);
                }

                if (Streams.ReadFully(EncStream, iv, 0, iv.Length) < iv.Length)
                {
                    throw new EndOfStreamException("unexpected end of stream.");
                }

                var v1 = this.EncStream.ReadByte();
                var v2 = this.EncStream.ReadByte();

                if (v1 < 0 || v2 < 0)
                {
                    throw new EndOfStreamException("unexpected end of stream.");
                }

                // Note: the oracle attack on the "quick check" bytes is deemed
                // a security risk for typical public key encryption usages,
                // therefore we do not perform the check.

                //				bool repeatCheckPassed =
                //					iv[iv.Length - 2] == (byte)v1
                //					&&	iv[iv.Length - 1] == (byte)v2;
                //
                //				// Note: some versions of PGP appear to produce 0 for the extra
                //				// bytes rather than repeating the two previous bytes
                //				bool zeroesCheckPassed =
                //					v1 == 0
                //					&&	v2 == 0;
                //
                //				if (!repeatCheckPassed && !zeroesCheckPassed)
                //				{
                //					throw new PgpDataValidationException("quick check failed.");
                //				}

                return(this.EncStream);
            }
            catch (PgpException)
            {
                throw;
            }
            catch (Exception e)
            {
                throw new PgpException("Exception starting decryption", e);
            }
        }