summary refs log tree commit diff
path: root/crypto/src/openpgp/PgpPublicKeyEncryptedData.cs
diff options
context:
space:
mode:
Diffstat (limited to 'crypto/src/openpgp/PgpPublicKeyEncryptedData.cs')
-rw-r--r--crypto/src/openpgp/PgpPublicKeyEncryptedData.cs140
1 files changed, 80 insertions, 60 deletions
diff --git a/crypto/src/openpgp/PgpPublicKeyEncryptedData.cs b/crypto/src/openpgp/PgpPublicKeyEncryptedData.cs
index b6504cbcd..c2a351182 100644
--- a/crypto/src/openpgp/PgpPublicKeyEncryptedData.cs
+++ b/crypto/src/openpgp/PgpPublicKeyEncryptedData.cs
@@ -1,10 +1,13 @@
 using System;
 using System.IO;
 
+using Org.BouncyCastle.Asn1.X9;
 using Org.BouncyCastle.Crypto;
 using Org.BouncyCastle.Crypto.IO;
+using Org.BouncyCastle.Crypto.Generators;
 using Org.BouncyCastle.Crypto.Parameters;
 using Org.BouncyCastle.Math;
+using Org.BouncyCastle.Math.EC;
 using Org.BouncyCastle.Security;
 using Org.BouncyCastle.Utilities.IO;
 
@@ -77,22 +80,29 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp
 		public SymmetricKeyAlgorithmTag GetSymmetricAlgorithm(
 			PgpPrivateKey privKey)
 		{
-			byte[] plain = fetchSymmetricKeyData(privKey);
+			byte[] sessionData = RecoverSessionData(privKey);
 
-			return (SymmetricKeyAlgorithmTag) plain[0];
+            return (SymmetricKeyAlgorithmTag)sessionData[0];
 		}
 
-		/// <summary>Return the decrypted data stream for the packet.</summary>
+        /// <summary>Return the decrypted data stream for the packet.</summary>
         public Stream GetDataStream(
             PgpPrivateKey privKey)
         {
-			byte[] plain = fetchSymmetricKeyData(privKey);
+			byte[] sessionData = RecoverSessionData(privKey);
 
-			IBufferedCipher c2;
-			string cipherName = PgpUtilities.GetSymmetricCipherName((SymmetricKeyAlgorithmTag) plain[0]);
+            if (!ConfirmCheckSum(sessionData))
+                throw new PgpKeyValidationException("key checksum failed");
+
+            SymmetricKeyAlgorithmTag symmAlg = (SymmetricKeyAlgorithmTag)sessionData[0];
+            if (symmAlg == SymmetricKeyAlgorithmTag.Null)
+                return encData.GetInputStream();
+
+            IBufferedCipher cipher;
+			string cipherName = PgpUtilities.GetSymmetricCipherName(symmAlg);
 			string cName = cipherName;
 
-			try
+            try
             {
                 if (encData is SymmetricEncIntegrityPacket)
                 {
@@ -103,7 +113,7 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp
 					cName += "/OpenPGPCFB/NoPadding";
                 }
 
-				c2 = CipherUtilities.GetCipher(cName);
+                cipher = CipherUtilities.GetCipher(cName);
 			}
             catch (PgpException e)
             {
@@ -114,19 +124,16 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp
                 throw new PgpException("exception creating cipher", e);
             }
 
-			if (c2 == null)
-				return encData.GetInputStream();
-
-			try
+            try
             {
 				KeyParameter key = ParameterUtilities.CreateKeyParameter(
-					cipherName, plain, 1, plain.Length - 3);
+					cipherName, sessionData, 1, sessionData.Length - 3);
 
-				byte[] iv = new byte[c2.GetBlockSize()];
+                byte[] iv = new byte[cipher.GetBlockSize()];
 
-				c2.Init(false, new ParametersWithIV(key, iv));
+                cipher.Init(false, new ParametersWithIV(key, iv));
 
-                encStream = BcpgInputStream.Wrap(new CipherStream(encData.GetInputStream(), c2, null));
+                encStream = BcpgInputStream.Wrap(new CipherStream(encData.GetInputStream(), cipher, null));
 
 				if (encData is SymmetricEncIntegrityPacket)
                 {
@@ -178,75 +185,88 @@ namespace Org.BouncyCastle.Bcpg.OpenPgp
             }
 		}
 
-		private byte[] fetchSymmetricKeyData(
-			PgpPrivateKey privKey)
+        private byte[] RecoverSessionData(PgpPrivateKey privKey)
 		{
-			IBufferedCipher c1 = GetKeyCipher(keyData.Algorithm);
+            byte[][] secKeyData = keyData.GetEncSessionKey();
+
+            if (keyData.Algorithm == PublicKeyAlgorithmTag.ECDH)
+            {
+                ECDHPublicBcpgKey ecKey = (ECDHPublicBcpgKey)privKey.PublicKeyPacket.Key;
+                X9ECParameters x9Params = ECKeyPairGenerator.FindECCurveByOid(ecKey.CurveOid);
+
+                byte[] enc = secKeyData[0];
+
+                int pLen = ((((enc[0] & 0xff) << 8) + (enc[1] & 0xff)) + 7) / 8;
+                byte[] pEnc = new byte[pLen];
+
+                Array.Copy(enc, 2, pEnc, 0, pLen);
+
+                byte[] keyEnc = new byte[enc[pLen + 2]];
+
+                Array.Copy(enc, 2 + pLen + 1, keyEnc, 0, keyEnc.Length);
+
+                ECPoint publicPoint = x9Params.Curve.DecodePoint(pEnc);
+
+                ECPrivateKeyParameters privKeyParams = (ECPrivateKeyParameters)privKey.Key;
+                ECPoint S = publicPoint.Multiply(privKeyParams.D).Normalize();
+
+                KeyParameter key = new KeyParameter(Rfc6637Utilities.CreateKey(privKey.PublicKeyPacket, S));
+
+                IWrapper w = PgpUtilities.CreateWrapper(ecKey.SymmetricKeyAlgorithm);
+                w.Init(false, key);
 
-			try
+                return PgpPad.UnpadSessionData(w.Unwrap(keyEnc, 0, keyEnc.Length));
+            }
+
+            IBufferedCipher cipher = GetKeyCipher(keyData.Algorithm);
+
+            try
 			{
-				c1.Init(false, privKey.Key);
+                cipher.Init(false, privKey.Key);
 			}
 			catch (InvalidKeyException e)
 			{
 				throw new PgpException("error setting asymmetric cipher", e);
 			}
 
-			BigInteger[] keyD = keyData.GetEncSessionKey();
-
-			if (keyData.Algorithm == PublicKeyAlgorithmTag.RsaEncrypt
+            if (keyData.Algorithm == PublicKeyAlgorithmTag.RsaEncrypt
 				|| keyData.Algorithm == PublicKeyAlgorithmTag.RsaGeneral)
 			{
-				c1.ProcessBytes(keyD[0].ToByteArrayUnsigned());
+                byte[] bi = secKeyData[0];
+
+                cipher.ProcessBytes(bi, 2, bi.Length - 2);
 			}
 			else
 			{
 				ElGamalPrivateKeyParameters k = (ElGamalPrivateKeyParameters)privKey.Key;
 				int size = (k.Parameters.P.BitLength + 7) / 8;
 
-				byte[] bi = keyD[0].ToByteArray();
-
-				int diff = bi.Length - size;
-				if (diff >= 0)
-				{
-					c1.ProcessBytes(bi, diff, size);
-				}
-				else
-				{
-					byte[] zeros = new byte[-diff];
-					c1.ProcessBytes(zeros);
-					c1.ProcessBytes(bi);
-				}
-
-				bi = keyD[1].ToByteArray();
-
-				diff = bi.Length - size;
-				if (diff >= 0)
-				{
-					c1.ProcessBytes(bi, diff, size);
-				}
-				else
-				{
-					byte[] zeros = new byte[-diff];
-					c1.ProcessBytes(zeros);
-					c1.ProcessBytes(bi);
-				}
+                ProcessEncodedMpi(cipher, size, secKeyData[0]);
+                ProcessEncodedMpi(cipher, size, secKeyData[1]);
 			}
 
-			byte[] plain;
-			try
+            try
 			{
-				plain = c1.DoFinal();
+                return cipher.DoFinal();
 			}
 			catch (Exception e)
 			{
 				throw new PgpException("exception decrypting secret key", e);
 			}
-
-			if (!ConfirmCheckSum(plain))
-				throw new PgpKeyValidationException("key checksum failed");
-
-			return plain;
 		}
+
+        private static void ProcessEncodedMpi(IBufferedCipher cipher, int size, byte[] mpiEnc)
+        {
+            if (mpiEnc.Length - 2 > size)  // leading Zero? Shouldn't happen but...
+            {
+                cipher.ProcessBytes(mpiEnc, 3, mpiEnc.Length - 3);
+            }
+            else
+            {
+                byte[] tmp = new byte[size];
+                Array.Copy(mpiEnc, 2, tmp, tmp.Length - (mpiEnc.Length - 2), mpiEnc.Length - 2);
+                cipher.ProcessBytes(tmp, 0, tmp.Length);
+            }
+        }
 	}
 }