summary refs log tree commit diff
path: root/crypto/src/openpgp/PgpSignature.cs
diff options
context:
space:
mode:
Diffstat (limited to 'crypto/src/openpgp/PgpSignature.cs')
-rw-r--r--crypto/src/openpgp/PgpSignature.cs422
1 files changed, 422 insertions, 0 deletions
diff --git a/crypto/src/openpgp/PgpSignature.cs b/crypto/src/openpgp/PgpSignature.cs
new file mode 100644
index 000000000..3bb6f2f0e
--- /dev/null
+++ b/crypto/src/openpgp/PgpSignature.cs
@@ -0,0 +1,422 @@
+using System;
+using System.IO;
+using Org.BouncyCastle.Asn1;
+
+using Org.BouncyCastle.Crypto;
+using Org.BouncyCastle.Security;
+using Org.BouncyCastle.Utilities;
+using Org.BouncyCastle.Utilities.Date;
+
+namespace Org.BouncyCastle.Bcpg.OpenPgp
+{
+	/// <remarks>A PGP signature object.</remarks>
+    public class PgpSignature
+    {
+        public const int BinaryDocument = 0x00;
+        public const int CanonicalTextDocument = 0x01;
+        public const int StandAlone = 0x02;
+
+        public const int DefaultCertification = 0x10;
+        public const int NoCertification = 0x11;
+        public const int CasualCertification = 0x12;
+        public const int PositiveCertification = 0x13;
+
+        public const int SubkeyBinding = 0x18;
+		public const int PrimaryKeyBinding = 0x19;
+		public const int DirectKey = 0x1f;
+        public const int KeyRevocation = 0x20;
+        public const int SubkeyRevocation = 0x28;
+        public const int CertificationRevocation = 0x30;
+        public const int Timestamp = 0x40;
+
+        private readonly SignaturePacket	sigPck;
+        private readonly int				signatureType;
+        private readonly TrustPacket		trustPck;
+
+		private ISigner	sig;
+		private byte	lastb; // Initial value anything but '\r'
+
+		internal PgpSignature(
+            BcpgInputStream bcpgInput)
+            : this((SignaturePacket)bcpgInput.ReadPacket())
+        {
+        }
+
+		internal PgpSignature(
+            SignaturePacket sigPacket)
+			: this(sigPacket, null)
+        {
+        }
+
+        internal PgpSignature(
+            SignaturePacket	sigPacket,
+            TrustPacket		trustPacket)
+        {
+			if (sigPacket == null)
+				throw new ArgumentNullException("sigPacket");
+
+			this.sigPck = sigPacket;
+			this.signatureType = sigPck.SignatureType;
+			this.trustPck = trustPacket;
+        }
+
+		private void GetSig()
+        {
+            this.sig = SignerUtilities.GetSigner(
+				PgpUtilities.GetSignatureName(sigPck.KeyAlgorithm, sigPck.HashAlgorithm));
+        }
+
+		/// <summary>The OpenPGP version number for this signature.</summary>
+		public int Version
+		{
+			get { return sigPck.Version; }
+		}
+
+		/// <summary>The key algorithm associated with this signature.</summary>
+		public PublicKeyAlgorithmTag KeyAlgorithm
+		{
+			get { return sigPck.KeyAlgorithm; }
+		}
+
+		/// <summary>The hash algorithm associated with this signature.</summary>
+		public HashAlgorithmTag HashAlgorithm
+		{
+			get { return sigPck.HashAlgorithm; }
+		}
+
+		public void InitVerify(
+            PgpPublicKey pubKey)
+        {
+			lastb = 0;
+            if (sig == null)
+            {
+                GetSig();
+            }
+            try
+            {
+                sig.Init(false, pubKey.GetKey());
+            }
+            catch (InvalidKeyException e)
+            {
+                throw new PgpException("invalid key.", e);
+            }
+        }
+
+        public void Update(
+            byte b)
+        {
+            if (signatureType == CanonicalTextDocument)
+            {
+				doCanonicalUpdateByte(b);
+            }
+            else
+            {
+                sig.Update(b);
+            }
+        }
+
+		private void doCanonicalUpdateByte(
+			byte b)
+		{
+			if (b == '\r')
+			{
+				doUpdateCRLF();
+			}
+			else if (b == '\n')
+			{
+				if (lastb != '\r')
+				{
+					doUpdateCRLF();
+				}
+			}
+			else
+			{
+				sig.Update(b);
+			}
+
+			lastb = b;
+		}
+
+		private void doUpdateCRLF()
+		{
+			sig.Update((byte)'\r');
+			sig.Update((byte)'\n');
+		}
+
+		public void Update(
+            params byte[] bytes)
+        {
+			Update(bytes, 0, bytes.Length);
+        }
+
+		public void Update(
+            byte[]	bytes,
+            int		off,
+            int		length)
+        {
+            if (signatureType == CanonicalTextDocument)
+            {
+                int finish = off + length;
+
+				for (int i = off; i != finish; i++)
+                {
+                    doCanonicalUpdateByte(bytes[i]);
+                }
+            }
+            else
+            {
+                sig.BlockUpdate(bytes, off, length);
+            }
+        }
+
+		public bool Verify()
+        {
+            byte[] trailer = GetSignatureTrailer();
+            sig.BlockUpdate(trailer, 0, trailer.Length);
+
+			return sig.VerifySignature(GetSignature());
+        }
+
+		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);
+		}
+
+		/// <summary>
+		/// Verify the signature as certifying the passed in public key as associated
+		/// with the passed in user attributes.
+		/// </summary>
+		/// <param name="userAttributes">User attributes the key was stored under.</param>
+		/// <param name="key">The key to be verified.</param>
+		/// <returns>True, if the signature matches, false otherwise.</returns>
+		public bool VerifyCertification(
+			PgpUserAttributeSubpacketVector	userAttributes,
+			PgpPublicKey					key)
+		{
+			UpdateWithPublicKey(key);
+
+			//
+			// hash in the userAttributes
+			//
+			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);
+			}
+
+			this.Update(sigPck.GetSignatureTrailer());
+
+			return sig.VerifySignature(this.GetSignature());
+		}
+
+		/// <summary>
+		/// Verify the signature as certifying the passed in public key as associated
+		/// with the passed in ID.
+		/// </summary>
+		/// <param name="id">ID the key was stored under.</param>
+		/// <param name="key">The key to be verified.</param>
+		/// <returns>True, if the signature matches, false otherwise.</returns>
+        public bool VerifyCertification(
+            string			id,
+            PgpPublicKey	key)
+        {
+			UpdateWithPublicKey(key);
+
+			//
+            // hash in the id
+            //
+            UpdateWithIdData(0xb4, Strings.ToUtf8ByteArray(id));
+
+			Update(sigPck.GetSignatureTrailer());
+
+			return sig.VerifySignature(GetSignature());
+        }
+
+		/// <summary>Verify a certification for the passed in key against the passed in master key.</summary>
+		/// <param name="masterKey">The key we are verifying against.</param>
+		/// <param name="pubKey">The key we are verifying.</param>
+		/// <returns>True, if the certification is valid, false otherwise.</returns>
+        public bool VerifyCertification(
+            PgpPublicKey	masterKey,
+            PgpPublicKey	pubKey)
+        {
+			UpdateWithPublicKey(masterKey);
+			UpdateWithPublicKey(pubKey);
+
+			Update(sigPck.GetSignatureTrailer());
+
+			return sig.VerifySignature(GetSignature());
+        }
+
+		/// <summary>Verify a key certification, such as revocation, for the passed in key.</summary>
+		/// <param name="pubKey">The key we are checking.</param>
+		/// <returns>True, if the certification is valid, false otherwise.</returns>
+        public bool VerifyCertification(
+            PgpPublicKey pubKey)
+        {
+            if (SignatureType != KeyRevocation
+                && SignatureType != SubkeyRevocation)
+            {
+                throw new InvalidOperationException("signature is not a key signature");
+            }
+
+			UpdateWithPublicKey(pubKey);
+
+            Update(sigPck.GetSignatureTrailer());
+
+			return sig.VerifySignature(GetSignature());
+        }
+
+		public int SignatureType
+        {
+			get { return sigPck.SignatureType; }
+        }
+
+		/// <summary>The ID of the key that created the signature.</summary>
+        public long KeyId
+        {
+            get { return sigPck.KeyId; }
+        }
+
+		[Obsolete("Use 'CreationTime' property instead")]
+		public DateTime GetCreationTime()
+		{
+			return CreationTime;
+		}
+
+		/// <summary>The creation time of this signature.</summary>
+        public DateTime CreationTime
+        {
+			get { return DateTimeUtilities.UnixMsToDateTime(sigPck.CreationTime); }
+        }
+
+		public byte[] GetSignatureTrailer()
+        {
+            return sigPck.GetSignatureTrailer();
+        }
+
+		/// <summary>
+		/// Return true if the signature has either hashed or unhashed subpackets.
+		/// </summary>
+		public bool HasSubpackets
+		{
+			get
+			{
+				return sigPck.GetHashedSubPackets() != null
+					|| sigPck.GetUnhashedSubPackets() != null;
+			}
+		}
+
+		public PgpSignatureSubpacketVector GetHashedSubPackets()
+        {
+            return createSubpacketVector(sigPck.GetHashedSubPackets());
+        }
+
+		public PgpSignatureSubpacketVector GetUnhashedSubPackets()
+        {
+            return createSubpacketVector(sigPck.GetUnhashedSubPackets());
+        }
+
+		private PgpSignatureSubpacketVector createSubpacketVector(SignatureSubpacket[] pcks)
+		{
+			return pcks == null ? null : new PgpSignatureSubpacketVector(pcks);
+		}
+
+		public byte[] GetSignature()
+        {
+            MPInteger[] sigValues = sigPck.GetSignature();
+            byte[] signature;
+
+			if (sigValues != null)
+			{
+				if (sigValues.Length == 1)    // an RSA signature
+				{
+					signature = sigValues[0].Value.ToByteArrayUnsigned();
+				}
+				else
+				{
+					try
+					{
+						signature = new DerSequence(
+							new DerInteger(sigValues[0].Value),
+							new DerInteger(sigValues[1].Value)).GetEncoded();
+					}
+					catch (IOException e)
+					{
+						throw new PgpException("exception encoding DSA sig.", e);
+					}
+				}
+			}
+			else
+			{
+				signature = sigPck.GetSignatureBytes();
+			}
+
+			return signature;
+        }
+
+		// TODO Handle the encoding stuff by subclassing BcpgObject?
+		public byte[] GetEncoded()
+        {
+            MemoryStream bOut = new MemoryStream();
+
+			Encode(bOut);
+
+			return bOut.ToArray();
+        }
+
+		public void Encode(
+            Stream outStream)
+        {
+            BcpgOutputStream bcpgOut = BcpgOutputStream.Wrap(outStream);
+
+			bcpgOut.WritePacket(sigPck);
+
+			if (trustPck != null)
+            {
+                bcpgOut.WritePacket(trustPck);
+            }
+        }
+
+		private byte[] GetEncodedPublicKey(
+			PgpPublicKey pubKey) 
+		{
+			try
+			{
+				return pubKey.publicPk.GetEncodedContents();
+			}
+			catch (IOException e)
+			{
+				throw new PgpException("exception preparing key.", e);
+			}
+		}
+    }
+}