summary refs log tree commit diff
path: root/crypto/src/openpgp/PgpPbeEncryptedData.cs
diff options
context:
space:
mode:
Diffstat (limited to 'crypto/src/openpgp/PgpPbeEncryptedData.cs')
-rw-r--r--crypto/src/openpgp/PgpPbeEncryptedData.cs135
1 files changed, 135 insertions, 0 deletions
diff --git a/crypto/src/openpgp/PgpPbeEncryptedData.cs b/crypto/src/openpgp/PgpPbeEncryptedData.cs
new file mode 100644
index 000000000..c5fe89407
--- /dev/null
+++ b/crypto/src/openpgp/PgpPbeEncryptedData.cs
@@ -0,0 +1,135 @@
+using System;
+using System.IO;
+
+using Org.BouncyCastle.Crypto;
+using Org.BouncyCastle.Crypto.IO;
+using Org.BouncyCastle.Crypto.Parameters;
+using Org.BouncyCastle.Security;
+using Org.BouncyCastle.Utilities.IO;
+
+namespace Org.BouncyCastle.Bcpg.OpenPgp
+{
+	/// <remarks>A password based encryption object.</remarks>
+    public class PgpPbeEncryptedData
+        : PgpEncryptedData
+    {
+        private readonly SymmetricKeyEncSessionPacket keyData;
+
+		internal PgpPbeEncryptedData(
+			SymmetricKeyEncSessionPacket	keyData,
+			InputStreamPacket				encData)
+			: base(encData)
+		{
+			this.keyData = keyData;
+		}
+
+		/// <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
+			{
+				SymmetricKeyAlgorithmTag keyAlgorithm = keyData.EncAlgorithm;
+
+				KeyParameter key = PgpUtilities.MakeKeyFromPassPhrase(
+					keyAlgorithm, keyData.S2k, passPhrase);
+
+
+				byte[] secKeyData = keyData.GetSecKeyData();
+				if (secKeyData != null && secKeyData.Length > 0)
+				{
+					IBufferedCipher keyCipher = CipherUtilities.GetCipher(
+						PgpUtilities.GetSymmetricCipherName(keyAlgorithm) + "/CFB/NoPadding");
+
+					keyCipher.Init(false,
+						new ParametersWithIV(key, new byte[keyCipher.GetBlockSize()]));
+
+					byte[] keyBytes = keyCipher.DoFinal(secKeyData);
+
+					keyAlgorithm = (SymmetricKeyAlgorithmTag) keyBytes[0];
+
+					key = ParameterUtilities.CreateKeyParameter(
+						PgpUtilities.GetSymmetricCipherName(keyAlgorithm),
+						keyBytes, 1, keyBytes.Length - 1);
+				}
+
+
+				IBufferedCipher c = CreateStreamCipher(keyAlgorithm);
+
+				byte[] 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);
+
+					string digestName = PgpUtilities.GetDigestName(HashAlgorithmTag.Sha1);
+					IDigest 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.");
+
+				int v1 = encStream.ReadByte();
+				int 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)
+
+				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 encStream;
+			}
+			catch (PgpException e)
+			{
+				throw e;
+			}
+			catch (Exception e)
+			{
+				throw new PgpException("Exception creating cipher", e);
+			}
+		}
+
+		private IBufferedCipher CreateStreamCipher(
+			SymmetricKeyAlgorithmTag keyAlgorithm)
+		{
+			string mode = (encData is SymmetricEncIntegrityPacket)
+				? "CFB"
+				: "OpenPGPCFB";
+
+			string cName = PgpUtilities.GetSymmetricCipherName(keyAlgorithm)
+				+ "/" + mode + "/NoPadding";
+
+			return CipherUtilities.GetCipher(cName);
+		}
+	}
+}