summary refs log tree commit diff
path: root/crypto/src/openpgp/PgpSignatureGenerator.cs
diff options
context:
space:
mode:
Diffstat (limited to 'crypto/src/openpgp/PgpSignatureGenerator.cs')
-rw-r--r--crypto/src/openpgp/PgpSignatureGenerator.cs393
1 files changed, 393 insertions, 0 deletions
diff --git a/crypto/src/openpgp/PgpSignatureGenerator.cs b/crypto/src/openpgp/PgpSignatureGenerator.cs
new file mode 100644
index 000000000..c5309689f
--- /dev/null
+++ b/crypto/src/openpgp/PgpSignatureGenerator.cs
@@ -0,0 +1,393 @@
+using System;
+using System.IO;
+
+using Org.BouncyCastle.Bcpg.Sig;
+using Org.BouncyCastle.Crypto;
+using Org.BouncyCastle.Crypto.Parameters;
+using Org.BouncyCastle.Math;
+using Org.BouncyCastle.Security;
+using Org.BouncyCastle.Utilities;
+
+namespace Org.BouncyCastle.Bcpg.OpenPgp
+{
+	/// <remarks>Generator for PGP signatures.</remarks>
+	// TODO Should be able to implement ISigner?
+    public class PgpSignatureGenerator
+    {
+		private static readonly SignatureSubpacket[] EmptySignatureSubpackets = new SignatureSubpacket[0];
+
+		private PublicKeyAlgorithmTag	keyAlgorithm;
+        private HashAlgorithmTag		hashAlgorithm;
+        private PgpPrivateKey			privKey;
+        private ISigner					sig;
+        private IDigest					dig;
+        private int						signatureType;
+        private byte					lastb;
+
+		private SignatureSubpacket[] unhashed = EmptySignatureSubpackets;
+        private SignatureSubpacket[] hashed = EmptySignatureSubpackets;
+
+		/// <summary>Create a generator for the passed in keyAlgorithm and hashAlgorithm codes.</summary>
+        public PgpSignatureGenerator(
+            PublicKeyAlgorithmTag	keyAlgorithm,
+            HashAlgorithmTag		hashAlgorithm)
+        {
+            this.keyAlgorithm = keyAlgorithm;
+            this.hashAlgorithm = hashAlgorithm;
+
+			dig = DigestUtilities.GetDigest(PgpUtilities.GetDigestName(hashAlgorithm));
+            sig = SignerUtilities.GetSigner(PgpUtilities.GetSignatureName(keyAlgorithm, hashAlgorithm));
+        }
+
+		/// <summary>Initialise the generator for signing.</summary>
+        public void InitSign(
+            int				sigType,
+            PgpPrivateKey	key)
+        {
+			InitSign(sigType, key, null);
+        }
+
+		/// <summary>Initialise the generator for signing.</summary>
+		public void InitSign(
+			int				sigType,
+			PgpPrivateKey	key,
+			SecureRandom	random)
+		{
+			this.privKey = key;
+			this.signatureType = sigType;
+
+			try
+			{
+				ICipherParameters cp = key.Key;
+				if (random != null)
+				{
+					cp = new ParametersWithRandom(key.Key, random);
+				}
+
+				sig.Init(true, cp);
+			}
+			catch (InvalidKeyException e)
+			{
+				throw new PgpException("invalid key.", e);
+			}
+
+			dig.Reset();
+			lastb = 0;
+		}
+
+		public void Update(
+            byte b)
+        {
+            if (signatureType == PgpSignature.CanonicalTextDocument)
+            {
+				doCanonicalUpdateByte(b);
+            }
+            else
+            {
+				doUpdateByte(b);
+            }
+        }
+
+		private void doCanonicalUpdateByte(
+			byte b)
+		{
+			if (b == '\r')
+			{
+				doUpdateCRLF();
+			}
+			else if (b == '\n')
+			{
+				if (lastb != '\r')
+				{
+					doUpdateCRLF();
+				}
+			}
+			else
+			{
+				doUpdateByte(b);
+			}
+
+			lastb = b;
+		}
+
+		private void doUpdateCRLF()
+		{
+			doUpdateByte((byte)'\r');
+			doUpdateByte((byte)'\n');
+		}
+
+		private void doUpdateByte(
+			byte b)
+		{
+			sig.Update(b);
+			dig.Update(b);
+		}
+
+		public void Update(
+            params byte[] b)
+        {
+			Update(b, 0, b.Length);
+        }
+
+		public void Update(
+            byte[]	b,
+            int		off,
+            int		len)
+        {
+            if (signatureType == PgpSignature.CanonicalTextDocument)
+            {
+                int finish = off + len;
+
+				for (int i = off; i != finish; i++)
+                {
+                    doCanonicalUpdateByte(b[i]);
+                }
+            }
+            else
+            {
+                sig.BlockUpdate(b, off, len);
+                dig.BlockUpdate(b, off, len);
+            }
+        }
+
+		public void SetHashedSubpackets(
+            PgpSignatureSubpacketVector hashedPackets)
+        {
+			hashed = hashedPackets == null
+				?	EmptySignatureSubpackets
+				:	hashedPackets.ToSubpacketArray();
+        }
+
+		public void SetUnhashedSubpackets(
+            PgpSignatureSubpacketVector unhashedPackets)
+        {
+			unhashed = unhashedPackets == null
+				?	EmptySignatureSubpackets
+				:	unhashedPackets.ToSubpacketArray();
+        }
+
+		/// <summary>Return the one pass header associated with the current signature.</summary>
+        public PgpOnePassSignature GenerateOnePassVersion(
+            bool isNested)
+        {
+            return new PgpOnePassSignature(
+				new OnePassSignaturePacket(
+					signatureType, hashAlgorithm, keyAlgorithm, privKey.KeyId, isNested));
+        }
+
+		/// <summary>Return a signature object containing the current signature state.</summary>
+        public PgpSignature Generate()
+        {
+			SignatureSubpacket[] hPkts = hashed, unhPkts = unhashed;
+
+			if (!packetPresent(hashed, SignatureSubpacketTag.CreationTime))
+			{
+				hPkts = insertSubpacket(hPkts, new SignatureCreationTime(false, DateTime.UtcNow));
+			}
+
+			if (!packetPresent(hashed, SignatureSubpacketTag.IssuerKeyId)
+				&& !packetPresent(unhashed, SignatureSubpacketTag.IssuerKeyId))
+			{
+				unhPkts = insertSubpacket(unhPkts, new IssuerKeyId(false, privKey.KeyId));
+			}
+
+			int version = 4;
+			byte[] hData;
+
+			try
+            {
+				MemoryStream hOut = new MemoryStream();
+
+				for (int i = 0; i != hPkts.Length; i++)
+				{
+					hPkts[i].Encode(hOut);
+				}
+
+				byte[] data = hOut.ToArray();
+
+				MemoryStream sOut = new MemoryStream(data.Length + 6);
+				sOut.WriteByte((byte)version);
+                sOut.WriteByte((byte)signatureType);
+                sOut.WriteByte((byte)keyAlgorithm);
+                sOut.WriteByte((byte)hashAlgorithm);
+				sOut.WriteByte((byte)(data.Length >> 8));
+                sOut.WriteByte((byte)data.Length);
+                sOut.Write(data, 0, data.Length);
+
+				hData = sOut.ToArray();
+			}
+            catch (IOException e)
+            {
+                throw new PgpException("exception encoding hashed data.", e);
+            }
+
+			sig.BlockUpdate(hData, 0, hData.Length);
+            dig.BlockUpdate(hData, 0, hData.Length);
+
+			hData = new byte[]
+			{
+				(byte) version,
+				0xff,
+				(byte)(hData.Length >> 24),
+				(byte)(hData.Length >> 16),
+				(byte)(hData.Length >> 8),
+				(byte) hData.Length
+			};
+
+			sig.BlockUpdate(hData, 0, hData.Length);
+            dig.BlockUpdate(hData, 0, hData.Length);
+
+			byte[] sigBytes = sig.GenerateSignature();
+			byte[] digest = DigestUtilities.DoFinal(dig);
+			byte[] fingerPrint = new byte[] { digest[0], digest[1] };
+
+			// an RSA signature
+			bool isRsa = keyAlgorithm == PublicKeyAlgorithmTag.RsaSign
+				|| keyAlgorithm == PublicKeyAlgorithmTag.RsaGeneral;
+
+			MPInteger[] sigValues = isRsa
+				?	PgpUtilities.RsaSigToMpi(sigBytes)
+				:	PgpUtilities.DsaSigToMpi(sigBytes);
+
+			return new PgpSignature(
+				new SignaturePacket(signatureType, privKey.KeyId, keyAlgorithm,
+					hashAlgorithm, hPkts, unhPkts, fingerPrint, sigValues));
+        }
+
+		/// <summary>Generate a certification for the passed in ID and key.</summary>
+		/// <param name="id">The ID we are certifying against the public key.</param>
+		/// <param name="pubKey">The key we are certifying against the ID.</param>
+		/// <returns>The certification.</returns>
+        public PgpSignature GenerateCertification(
+            string			id,
+            PgpPublicKey	pubKey)
+        {
+			UpdateWithPublicKey(pubKey);
+
+			//
+            // hash in the id
+            //
+			UpdateWithIdData(0xb4, Strings.ToUtf8ByteArray(id));
+
+            return Generate();
+        }
+
+		/// <summary>Generate a certification for the passed in userAttributes.</summary>
+		/// <param name="userAttributes">The ID we are certifying against the public key.</param>
+		/// <param name="pubKey">The key we are certifying against the ID.</param>
+		/// <returns>The certification.</returns>
+		public PgpSignature GenerateCertification(
+			PgpUserAttributeSubpacketVector	userAttributes,
+			PgpPublicKey					pubKey)
+		{
+			UpdateWithPublicKey(pubKey);
+
+			//
+			// hash in the attributes
+			//
+			try
+			{
+				MemoryStream bOut = new MemoryStream();
+				foreach (UserAttributeSubpacket packet in userAttributes.ToSubpacketArray())
+				{
+					packet.Encode(bOut);
+				}
+				UpdateWithIdData(0xd1, bOut.ToArray());
+			}
+			catch (IOException e)
+			{
+				throw new PgpException("cannot encode subpacket array", e);
+			}
+
+			return this.Generate();
+		}
+
+		/// <summary>Generate a certification for the passed in key against the passed in master key.</summary>
+		/// <param name="masterKey">The key we are certifying against.</param>
+		/// <param name="pubKey">The key we are certifying.</param>
+		/// <returns>The certification.</returns>
+        public PgpSignature GenerateCertification(
+            PgpPublicKey	masterKey,
+            PgpPublicKey	pubKey)
+        {
+			UpdateWithPublicKey(masterKey);
+			UpdateWithPublicKey(pubKey);
+
+			return Generate();
+        }
+
+		/// <summary>Generate a certification, such as a revocation, for the passed in key.</summary>
+		/// <param name="pubKey">The key we are certifying.</param>
+		/// <returns>The certification.</returns>
+        public PgpSignature GenerateCertification(
+            PgpPublicKey pubKey)
+        {
+			UpdateWithPublicKey(pubKey);
+
+			return Generate();
+        }
+
+		private byte[] GetEncodedPublicKey(
+			PgpPublicKey pubKey) 
+		{
+			try
+			{
+				return pubKey.publicPk.GetEncodedContents();
+			}
+			catch (IOException e)
+			{
+				throw new PgpException("exception preparing key.", e);
+			}
+		}
+
+		private bool packetPresent(
+			SignatureSubpacket[]	packets,
+			SignatureSubpacketTag	type)
+		{
+			for (int i = 0; i != packets.Length; i++)
+			{
+				if (packets[i].SubpacketType == type)
+				{
+					return true;
+				}
+			}
+
+			return false;
+		}
+
+		private SignatureSubpacket[] insertSubpacket(
+			SignatureSubpacket[]	packets,
+			SignatureSubpacket		subpacket)
+		{
+			SignatureSubpacket[] tmp = new SignatureSubpacket[packets.Length + 1];
+			tmp[0] = subpacket;
+			packets.CopyTo(tmp, 1);
+			return tmp;
+		}
+
+		private void UpdateWithIdData(
+			int		header,
+			byte[]	idBytes)
+		{
+			this.Update(
+				(byte) header,
+				(byte)(idBytes.Length >> 24),
+				(byte)(idBytes.Length >> 16),
+				(byte)(idBytes.Length >> 8),
+				(byte)(idBytes.Length));
+			this.Update(idBytes);
+		}
+
+		private void UpdateWithPublicKey(
+			PgpPublicKey key)
+		{
+			byte[] keyBytes = GetEncodedPublicKey(key);
+
+			this.Update(
+				(byte) 0x99,
+				(byte)(keyBytes.Length >> 8),
+				(byte)(keyBytes.Length));
+			this.Update(keyBytes);
+		}
+	}
+}