summary refs log tree commit diff
path: root/crypto/src/openpgp/PgpUtilities.cs
diff options
context:
space:
mode:
Diffstat (limited to 'crypto/src/openpgp/PgpUtilities.cs')
-rw-r--r--crypto/src/openpgp/PgpUtilities.cs426
1 files changed, 426 insertions, 0 deletions
diff --git a/crypto/src/openpgp/PgpUtilities.cs b/crypto/src/openpgp/PgpUtilities.cs
new file mode 100644
index 000000000..7cd3dfc4f
--- /dev/null
+++ b/crypto/src/openpgp/PgpUtilities.cs
@@ -0,0 +1,426 @@
+using System;
+using System.IO;
+using System.Text;
+
+using Org.BouncyCastle.Asn1;
+using Org.BouncyCastle.Crypto;
+using Org.BouncyCastle.Crypto.Parameters;
+using Org.BouncyCastle.Math;
+using Org.BouncyCastle.Security;
+using Org.BouncyCastle.Utilities;
+using Org.BouncyCastle.Utilities.Encoders;
+using Org.BouncyCastle.Utilities.IO;
+
+namespace Org.BouncyCastle.Bcpg.OpenPgp
+{
+	/// <remarks>Basic utility class.</remarks>
+    public sealed class PgpUtilities
+    {
+        private PgpUtilities()
+        {
+        }
+
+		public static MPInteger[] DsaSigToMpi(
+			byte[] encoding)
+		{
+			DerInteger i1, i2;
+
+			try
+			{
+				Asn1Sequence s = (Asn1Sequence) Asn1Object.FromByteArray(encoding);
+
+				i1 = (DerInteger) s[0];
+				i2 = (DerInteger) s[1];
+			}
+			catch (IOException e)
+			{
+				throw new PgpException("exception encoding signature", e);
+			}
+
+			return new MPInteger[]{ new MPInteger(i1.Value), new MPInteger(i2.Value) };
+		}
+
+		public static MPInteger[] RsaSigToMpi(
+			byte[] encoding)
+		{
+			return new MPInteger[]{ new MPInteger(new BigInteger(1, encoding)) };
+		}
+
+		public static string GetDigestName(
+            HashAlgorithmTag hashAlgorithm)
+        {
+            switch (hashAlgorithm)
+            {
+				case HashAlgorithmTag.Sha1:
+					return "SHA1";
+				case HashAlgorithmTag.MD2:
+					return "MD2";
+				case HashAlgorithmTag.MD5:
+					return "MD5";
+				case HashAlgorithmTag.RipeMD160:
+					return "RIPEMD160";
+				case HashAlgorithmTag.Sha224:
+					return "SHA224";
+				case HashAlgorithmTag.Sha256:
+					return "SHA256";
+				case HashAlgorithmTag.Sha384:
+					return "SHA384";
+				case HashAlgorithmTag.Sha512:
+					return "SHA512";
+				default:
+					throw new PgpException("unknown hash algorithm tag in GetDigestName: " + hashAlgorithm);
+			}
+        }
+
+		public static string GetSignatureName(
+            PublicKeyAlgorithmTag	keyAlgorithm,
+            HashAlgorithmTag		hashAlgorithm)
+        {
+            string encAlg;
+			switch (keyAlgorithm)
+            {
+				case PublicKeyAlgorithmTag.RsaGeneral:
+				case PublicKeyAlgorithmTag.RsaSign:
+					encAlg = "RSA";
+					break;
+				case PublicKeyAlgorithmTag.Dsa:
+					encAlg = "DSA";
+					break;
+				case PublicKeyAlgorithmTag.ElGamalEncrypt: // in some malformed cases.
+				case PublicKeyAlgorithmTag.ElGamalGeneral:
+					encAlg = "ElGamal";
+					break;
+				default:
+					throw new PgpException("unknown algorithm tag in signature:" + keyAlgorithm);
+            }
+
+			return GetDigestName(hashAlgorithm) + "with" + encAlg;
+        }
+
+		public static string GetSymmetricCipherName(
+            SymmetricKeyAlgorithmTag algorithm)
+        {
+            switch (algorithm)
+            {
+				case SymmetricKeyAlgorithmTag.Null:
+					return null;
+				case SymmetricKeyAlgorithmTag.TripleDes:
+					return "DESEDE";
+				case SymmetricKeyAlgorithmTag.Idea:
+					return "IDEA";
+				case SymmetricKeyAlgorithmTag.Cast5:
+					return "CAST5";
+				case SymmetricKeyAlgorithmTag.Blowfish:
+					return "Blowfish";
+				case SymmetricKeyAlgorithmTag.Safer:
+					return "SAFER";
+				case SymmetricKeyAlgorithmTag.Des:
+					return "DES";
+				case SymmetricKeyAlgorithmTag.Aes128:
+					return "AES";
+				case SymmetricKeyAlgorithmTag.Aes192:
+					return "AES";
+				case SymmetricKeyAlgorithmTag.Aes256:
+					return "AES";
+				case SymmetricKeyAlgorithmTag.Twofish:
+					return "Twofish";
+				default:
+					throw new PgpException("unknown symmetric algorithm: " + algorithm);
+            }
+        }
+
+		public static int GetKeySize(SymmetricKeyAlgorithmTag algorithm)
+        {
+            int keySize;
+            switch (algorithm)
+            {
+                case SymmetricKeyAlgorithmTag.Des:
+                    keySize = 64;
+                    break;
+                case SymmetricKeyAlgorithmTag.Idea:
+                case SymmetricKeyAlgorithmTag.Cast5:
+                case SymmetricKeyAlgorithmTag.Blowfish:
+                case SymmetricKeyAlgorithmTag.Safer:
+                case SymmetricKeyAlgorithmTag.Aes128:
+                    keySize = 128;
+                    break;
+                case SymmetricKeyAlgorithmTag.TripleDes:
+                case SymmetricKeyAlgorithmTag.Aes192:
+                    keySize = 192;
+                    break;
+                case SymmetricKeyAlgorithmTag.Aes256:
+                case SymmetricKeyAlgorithmTag.Twofish:
+                    keySize = 256;
+                    break;
+                default:
+                    throw new PgpException("unknown symmetric algorithm: " + algorithm);
+            }
+
+			return keySize;
+        }
+
+		public static KeyParameter MakeKey(
+			SymmetricKeyAlgorithmTag	algorithm,
+			byte[]						keyBytes)
+		{
+			string algName = GetSymmetricCipherName(algorithm);
+
+			return ParameterUtilities.CreateKeyParameter(algName, keyBytes);
+		}
+
+		public static KeyParameter MakeRandomKey(
+            SymmetricKeyAlgorithmTag	algorithm,
+            SecureRandom				random)
+        {
+            int keySize = GetKeySize(algorithm);
+            byte[] keyBytes = new byte[(keySize + 7) / 8];
+            random.NextBytes(keyBytes);
+			return MakeKey(algorithm, keyBytes);
+        }
+
+		public static KeyParameter MakeKeyFromPassPhrase(
+            SymmetricKeyAlgorithmTag	algorithm,
+            S2k							s2k,
+            char[]						passPhrase)
+        {
+			int keySize = GetKeySize(algorithm);
+			byte[] pBytes = Strings.ToByteArray(new string(passPhrase));
+			byte[] keyBytes = new byte[(keySize + 7) / 8];
+
+			int generatedBytes = 0;
+            int loopCount = 0;
+
+			while (generatedBytes < keyBytes.Length)
+            {
+				IDigest digest;
+				if (s2k != null)
+                {
+					string digestName = GetDigestName(s2k.HashAlgorithm);
+
+                    try
+                    {
+						digest = DigestUtilities.GetDigest(digestName);
+                    }
+                    catch (Exception e)
+                    {
+                        throw new PgpException("can't find S2k digest", e);
+                    }
+
+					for (int i = 0; i != loopCount; i++)
+                    {
+                        digest.Update(0);
+                    }
+
+					byte[] iv = s2k.GetIV();
+
+					switch (s2k.Type)
+                    {
+						case S2k.Simple:
+							digest.BlockUpdate(pBytes, 0, pBytes.Length);
+							break;
+						case S2k.Salted:
+							digest.BlockUpdate(iv, 0, iv.Length);
+							digest.BlockUpdate(pBytes, 0, pBytes.Length);
+							break;
+						case S2k.SaltedAndIterated:
+							long count = s2k.IterationCount;
+							digest.BlockUpdate(iv, 0, iv.Length);
+							digest.BlockUpdate(pBytes, 0, pBytes.Length);
+
+							count -= iv.Length + pBytes.Length;
+
+							while (count > 0)
+							{
+								if (count < iv.Length)
+								{
+									digest.BlockUpdate(iv, 0, (int)count);
+									break;
+								}
+								else
+								{
+									digest.BlockUpdate(iv, 0, iv.Length);
+									count -= iv.Length;
+								}
+
+								if (count < pBytes.Length)
+								{
+									digest.BlockUpdate(pBytes, 0, (int)count);
+									count = 0;
+								}
+								else
+								{
+									digest.BlockUpdate(pBytes, 0, pBytes.Length);
+									count -= pBytes.Length;
+								}
+							}
+							break;
+						default:
+							throw new PgpException("unknown S2k type: " + s2k.Type);
+                    }
+                }
+                else
+                {
+                    try
+                    {
+                        digest = DigestUtilities.GetDigest("MD5");
+
+						for (int i = 0; i != loopCount; i++)
+                        {
+                            digest.Update(0);
+                        }
+
+						digest.BlockUpdate(pBytes, 0, pBytes.Length);
+                    }
+                    catch (Exception e)
+                    {
+                        throw new PgpException("can't find MD5 digest", e);
+                    }
+                }
+
+				byte[] dig = DigestUtilities.DoFinal(digest);
+
+				if (dig.Length > (keyBytes.Length - generatedBytes))
+                {
+                    Array.Copy(dig, 0, keyBytes, generatedBytes, keyBytes.Length - generatedBytes);
+                }
+                else
+                {
+                    Array.Copy(dig, 0, keyBytes, generatedBytes, dig.Length);
+                }
+
+				generatedBytes += dig.Length;
+
+				loopCount++;
+            }
+
+			Array.Clear(pBytes, 0, pBytes.Length);
+
+			return MakeKey(algorithm, keyBytes);
+        }
+
+		/// <summary>Write out the passed in file as a literal data packet.</summary>
+        public static void WriteFileToLiteralData(
+            Stream		output,
+            char		fileType,
+            FileInfo	file)
+        {
+            PgpLiteralDataGenerator lData = new PgpLiteralDataGenerator();
+			Stream pOut = lData.Open(output, fileType, file.Name, file.Length, file.LastWriteTime);
+			PipeFileContents(file, pOut, 4096);
+        }
+
+		/// <summary>Write out the passed in file as a literal data packet in partial packet format.</summary>
+        public static void WriteFileToLiteralData(
+            Stream		output,
+            char		fileType,
+            FileInfo	file,
+            byte[]		buffer)
+        {
+            PgpLiteralDataGenerator lData = new PgpLiteralDataGenerator();
+            Stream pOut = lData.Open(output, fileType, file.Name, file.LastWriteTime, buffer);
+			PipeFileContents(file, pOut, buffer.Length);
+        }
+
+		private static void PipeFileContents(FileInfo file, Stream pOut, int bufSize)
+		{
+			FileStream inputStream = file.OpenRead();
+			byte[] buf = new byte[bufSize];
+
+			int len;
+            while ((len = inputStream.Read(buf, 0, buf.Length)) > 0)
+            {
+                pOut.Write(buf, 0, len);
+            }
+
+			pOut.Close();
+			inputStream.Close();
+		}
+
+		private const int ReadAhead = 60;
+
+		private static bool IsPossiblyBase64(
+            int ch)
+        {
+            return (ch >= 'A' && ch <= 'Z') || (ch >= 'a' && ch <= 'z')
+                    || (ch >= '0' && ch <= '9') || (ch == '+') || (ch == '/')
+                    || (ch == '\r') || (ch == '\n');
+        }
+
+		/// <summary>
+		/// Return either an ArmoredInputStream or a BcpgInputStream based on whether
+		/// the initial characters of the stream are binary PGP encodings or not.
+		/// </summary>
+        public static Stream GetDecoderStream(
+            Stream inputStream)
+        {
+			// TODO Remove this restriction?
+			if (!inputStream.CanSeek)
+				throw new ArgumentException("inputStream must be seek-able", "inputStream");
+
+			long markedPos = inputStream.Position;
+
+			int ch = inputStream.ReadByte();
+            if ((ch & 0x80) != 0)
+            {
+                inputStream.Position = markedPos;
+
+				return inputStream;
+            }
+            else
+            {
+                if (!IsPossiblyBase64(ch))
+                {
+                    inputStream.Position = markedPos;
+
+					return new ArmoredInputStream(inputStream);
+                }
+
+				byte[]	buf = new byte[ReadAhead];
+                int		count = 1;
+                int		index = 1;
+
+				buf[0] = (byte)ch;
+                while (count != ReadAhead && (ch = inputStream.ReadByte()) >= 0)
+                {
+                    if (!IsPossiblyBase64(ch))
+                    {
+                        inputStream.Position = markedPos;
+
+						return new ArmoredInputStream(inputStream);
+                    }
+
+					if (ch != '\n' && ch != '\r')
+                    {
+                        buf[index++] = (byte)ch;
+                    }
+
+					count++;
+                }
+
+				inputStream.Position = markedPos;
+
+				//
+                // nothing but new lines, little else, assume regular armoring
+                //
+                if (count < 4)
+                {
+                    return new ArmoredInputStream(inputStream);
+                }
+
+				//
+                // test our non-blank data
+                //
+                byte[] firstBlock = new byte[8];
+				Array.Copy(buf, 0, firstBlock, 0, firstBlock.Length);
+				byte[] decoded = Base64.Decode(firstBlock);
+
+				//
+                // it's a base64 PGP block.
+                //
+				bool hasHeaders = (decoded[0] & 0x80) == 0;
+
+				return new ArmoredInputStream(inputStream, hasHeaders);
+            }
+        }
+    }
+}