452 lines
		
	
	
		
			9.2 KiB
		
	
	
	
		
			C#
		
	
	
	
	
	
			
		
		
	
	
			452 lines
		
	
	
		
			9.2 KiB
		
	
	
	
		
			C#
		
	
	
	
	
	
| using System;
 | |
| 
 | |
| using Org.BouncyCastle.Crypto;
 | |
| using Org.BouncyCastle.Crypto.Digests;
 | |
| using Org.BouncyCastle.Crypto.Parameters;
 | |
| 
 | |
| namespace Org.BouncyCastle.Crypto.Signers
 | |
| {
 | |
| 
 | |
| 	/// <summary> ISO9796-2 - mechanism using a hash function with recovery (scheme 1)</summary>
 | |
| 	public class Iso9796d2Signer : ISignerWithRecovery
 | |
| 	{
 | |
| 		/// <summary>
 | |
| 		/// Return a reference to the recoveredMessage message.
 | |
| 		/// </summary>
 | |
| 		/// <returns>The full/partial recoveredMessage message.</returns>
 | |
| 		/// <seealso cref="ISignerWithRecovery.GetRecoveredMessage"/>
 | |
| 		public byte[] GetRecoveredMessage()
 | |
| 		{
 | |
| 			return recoveredMessage;
 | |
| 		}
 | |
| 
 | |
| 		public const int TrailerImplicit = 0xBC;
 | |
| 		public const int TrailerRipeMD160 = 0x31CC;
 | |
| 		public const int TrailerRipeMD128 = 0x32CC;
 | |
| 		public const int TrailerSha1 = 0x33CC;
 | |
| 
 | |
| 		private IDigest digest;
 | |
| 		private IAsymmetricBlockCipher cipher;
 | |
| 
 | |
| 		private int trailer;
 | |
| 		private int keyBits;
 | |
| 		private byte[] block;
 | |
| 		private byte[] mBuf;
 | |
| 		private int messageLength;
 | |
| 		private bool fullMessage;
 | |
| 		private byte[] recoveredMessage;
 | |
| 
 | |
| 		/// <summary>
 | |
| 		/// Generate a signer for the with either implicit or explicit trailers
 | |
| 		/// for ISO9796-2.
 | |
| 		/// </summary>
 | |
| 		/// <param name="cipher">base cipher to use for signature creation/verification</param>
 | |
| 		/// <param name="digest">digest to use.</param>
 | |
| 		/// <param name="isImplicit">whether or not the trailer is implicit or gives the hash.</param>
 | |
| 		public Iso9796d2Signer(
 | |
| 			IAsymmetricBlockCipher	cipher,
 | |
| 			IDigest					digest,
 | |
| 			bool					isImplicit)
 | |
| 		{
 | |
| 			this.cipher = cipher;
 | |
| 			this.digest = digest;
 | |
| 
 | |
| 			if (isImplicit)
 | |
| 			{
 | |
| 				trailer = TrailerImplicit;
 | |
| 			}
 | |
| 			else
 | |
| 			{
 | |
| 				if (digest is Sha1Digest)
 | |
| 				{
 | |
| 					trailer = TrailerSha1;
 | |
| 				}
 | |
| 				else if (digest is RipeMD160Digest)
 | |
| 				{
 | |
| 					trailer = TrailerRipeMD160;
 | |
| 				}
 | |
| 				else if (digest is RipeMD128Digest)
 | |
| 				{
 | |
| 					trailer = TrailerRipeMD128;
 | |
| 				}
 | |
| 				else
 | |
| 				{
 | |
| 					throw new System.ArgumentException("no valid trailer for digest");
 | |
| 				}
 | |
| 			}
 | |
| 		}
 | |
| 
 | |
| 		/// <summary> Constructor for a signer with an explicit digest trailer.
 | |
| 		///
 | |
| 		/// </summary>
 | |
| 		/// <param name="cipher">cipher to use.
 | |
| 		/// </param>
 | |
| 		/// <param name="digest">digest to sign with.
 | |
| 		/// </param>
 | |
| 		public Iso9796d2Signer(IAsymmetricBlockCipher cipher, IDigest digest):this(cipher, digest, false)
 | |
| 		{
 | |
| 		}
 | |
| 
 | |
| 		public string AlgorithmName
 | |
| 		{
 | |
| 			get { return digest.AlgorithmName + "with" + "ISO9796-2S1"; }
 | |
| 		}
 | |
| 
 | |
| 		public virtual void Init(bool forSigning, ICipherParameters parameters)
 | |
| 		{
 | |
| 			RsaKeyParameters kParam = (RsaKeyParameters) parameters;
 | |
| 
 | |
| 			cipher.Init(forSigning, kParam);
 | |
| 
 | |
| 			keyBits = kParam.Modulus.BitLength;
 | |
| 
 | |
| 			block = new byte[(keyBits + 7) / 8];
 | |
| 			if (trailer == TrailerImplicit)
 | |
| 			{
 | |
| 				mBuf = new byte[block.Length - digest.GetDigestSize() - 2];
 | |
| 			}
 | |
| 			else
 | |
| 			{
 | |
| 				mBuf = new byte[block.Length - digest.GetDigestSize() - 3];
 | |
| 			}
 | |
| 
 | |
| 			Reset();
 | |
| 		}
 | |
| 
 | |
| 		/// <summary> compare two byte arrays.</summary>
 | |
| 		private bool IsSameAs(byte[] a, byte[] b)
 | |
| 		{
 | |
| 			if (messageLength > mBuf.Length)
 | |
| 			{
 | |
| 				if (mBuf.Length > b.Length)
 | |
| 				{
 | |
| 					return false;
 | |
| 				}
 | |
| 
 | |
| 				for (int i = 0; i != mBuf.Length; i++)
 | |
| 				{
 | |
| 					if (a[i] != b[i])
 | |
| 					{
 | |
| 						return false;
 | |
| 					}
 | |
| 				}
 | |
| 			}
 | |
| 			else
 | |
| 			{
 | |
| 				if (messageLength != b.Length)
 | |
| 				{
 | |
| 					return false;
 | |
| 				}
 | |
| 
 | |
| 				for (int i = 0; i != b.Length; i++)
 | |
| 				{
 | |
| 					if (a[i] != b[i])
 | |
| 					{
 | |
| 						return false;
 | |
| 					}
 | |
| 				}
 | |
| 			}
 | |
| 
 | |
| 			return true;
 | |
| 		}
 | |
| 
 | |
| 		/// <summary> clear possible sensitive data</summary>
 | |
| 		private void  ClearBlock(
 | |
| 			byte[] block)
 | |
| 		{
 | |
| 			Array.Clear(block, 0, block.Length);
 | |
| 		}
 | |
| 
 | |
| 		/// <summary> update the internal digest with the byte b</summary>
 | |
| 		public void Update(
 | |
| 			byte input)
 | |
| 		{
 | |
| 			digest.Update(input);
 | |
| 
 | |
| 			if (messageLength < mBuf.Length)
 | |
| 			{
 | |
| 				mBuf[messageLength] = input;
 | |
| 			}
 | |
| 
 | |
| 			messageLength++;
 | |
| 		}
 | |
| 
 | |
| 		/// <summary> update the internal digest with the byte array in</summary>
 | |
| 		public void BlockUpdate(
 | |
| 			byte[]	input,
 | |
| 			int		inOff,
 | |
| 			int		length)
 | |
| 		{
 | |
| 			digest.BlockUpdate(input, inOff, length);
 | |
| 
 | |
| 			if (messageLength < mBuf.Length)
 | |
| 			{
 | |
| 				for (int i = 0; i < length && (i + messageLength) < mBuf.Length; i++)
 | |
| 				{
 | |
| 					mBuf[messageLength + i] = input[inOff + i];
 | |
| 				}
 | |
| 			}
 | |
| 
 | |
| 			messageLength += length;
 | |
| 		}
 | |
| 
 | |
| 		/// <summary> reset the internal state</summary>
 | |
| 		public virtual void Reset()
 | |
| 		{
 | |
| 			digest.Reset();
 | |
| 			messageLength = 0;
 | |
| 			ClearBlock(mBuf);
 | |
| 
 | |
| 			if (recoveredMessage != null)
 | |
| 			{
 | |
| 				ClearBlock(recoveredMessage);
 | |
| 			}
 | |
| 
 | |
| 			recoveredMessage = null;
 | |
| 			fullMessage = false;
 | |
| 		}
 | |
| 
 | |
| 		/// <summary> Generate a signature for the loaded message using the key we were
 | |
| 		/// initialised with.
 | |
| 		/// </summary>
 | |
| 		public virtual byte[] GenerateSignature()
 | |
| 		{
 | |
| 			int digSize = digest.GetDigestSize();
 | |
| 
 | |
| 			int t = 0;
 | |
| 			int delta = 0;
 | |
| 
 | |
| 			if (trailer == TrailerImplicit)
 | |
| 			{
 | |
| 				t = 8;
 | |
| 				delta = block.Length - digSize - 1;
 | |
| 				digest.DoFinal(block, delta);
 | |
| 				block[block.Length - 1] = (byte) TrailerImplicit;
 | |
| 			}
 | |
| 			else
 | |
| 			{
 | |
| 				t = 16;
 | |
| 				delta = block.Length - digSize - 2;
 | |
| 				digest.DoFinal(block, delta);
 | |
| 				block[block.Length - 2] = (byte) ((uint)trailer >> 8);
 | |
| 				block[block.Length - 1] = (byte) trailer;
 | |
| 			}
 | |
| 
 | |
| 			byte header = 0;
 | |
| 			int x = (digSize + messageLength) * 8 + t + 4 - keyBits;
 | |
| 
 | |
| 			if (x > 0)
 | |
| 			{
 | |
| 				int mR = messageLength - ((x + 7) / 8);
 | |
| 				header = (byte) (0x60);
 | |
| 
 | |
| 				delta -= mR;
 | |
| 
 | |
| 				Array.Copy(mBuf, 0, block, delta, mR);
 | |
| 			}
 | |
| 			else
 | |
| 			{
 | |
| 				header = (byte) (0x40);
 | |
| 				delta -= messageLength;
 | |
| 
 | |
| 				Array.Copy(mBuf, 0, block, delta, messageLength);
 | |
| 			}
 | |
| 
 | |
| 			if ((delta - 1) > 0)
 | |
| 			{
 | |
| 				for (int i = delta - 1; i != 0; i--)
 | |
| 				{
 | |
| 					block[i] = (byte) 0xbb;
 | |
| 				}
 | |
| 				block[delta - 1] ^= (byte) 0x01;
 | |
| 				block[0] = (byte) 0x0b;
 | |
| 				block[0] |= header;
 | |
| 			}
 | |
| 			else
 | |
| 			{
 | |
| 				block[0] = (byte) 0x0a;
 | |
| 				block[0] |= header;
 | |
| 			}
 | |
| 
 | |
| 			byte[] b = cipher.ProcessBlock(block, 0, block.Length);
 | |
| 
 | |
| 			ClearBlock(mBuf);
 | |
| 			ClearBlock(block);
 | |
| 
 | |
| 			return b;
 | |
| 		}
 | |
| 
 | |
| 		/// <summary> return true if the signature represents a ISO9796-2 signature
 | |
| 		/// for the passed in message.
 | |
| 		/// </summary>
 | |
| 		public virtual bool VerifySignature(byte[] signature)
 | |
| 		{
 | |
| 			byte[] block = cipher.ProcessBlock(signature, 0, signature.Length);
 | |
| 
 | |
| 			if (((block[0] & 0xC0) ^ 0x40) != 0)
 | |
| 			{
 | |
| 				ClearBlock(mBuf);
 | |
| 				ClearBlock(block);
 | |
| 
 | |
| 				return false;
 | |
| 			}
 | |
| 
 | |
| 			if (((block[block.Length - 1] & 0xF) ^ 0xC) != 0)
 | |
| 			{
 | |
| 				ClearBlock(mBuf);
 | |
| 				ClearBlock(block);
 | |
| 
 | |
| 				return false;
 | |
| 			}
 | |
| 
 | |
| 			int delta = 0;
 | |
| 
 | |
| 			if (((block[block.Length - 1] & 0xFF) ^ 0xBC) == 0)
 | |
| 			{
 | |
| 				delta = 1;
 | |
| 			}
 | |
| 			else
 | |
| 			{
 | |
| 				int sigTrail = ((block[block.Length - 2] & 0xFF) << 8) | (block[block.Length - 1] & 0xFF);
 | |
| 
 | |
| 				switch (sigTrail)
 | |
| 				{
 | |
| 					case TrailerRipeMD160:
 | |
| 						if (!(digest is RipeMD160Digest))
 | |
| 						{
 | |
| 							throw new ArgumentException("signer should be initialised with RipeMD160");
 | |
| 						}
 | |
| 						break;
 | |
| 					case TrailerSha1:
 | |
| 						if (!(digest is Sha1Digest))
 | |
| 						{
 | |
| 							throw new ArgumentException("signer should be initialised with SHA1");
 | |
| 						}
 | |
| 						break;
 | |
| 					case TrailerRipeMD128:
 | |
| 						if (!(digest is RipeMD128Digest))
 | |
| 						{
 | |
| 							throw new ArgumentException("signer should be initialised with RipeMD128");
 | |
| 						}
 | |
| 						break;
 | |
| 					default:
 | |
| 						throw new ArgumentException("unrecognised hash in signature");
 | |
| 				}
 | |
| 
 | |
| 				delta = 2;
 | |
| 			}
 | |
| 
 | |
| 			//
 | |
| 			// find out how much padding we've got
 | |
| 			//
 | |
| 			int mStart = 0;
 | |
| 			for (; mStart != block.Length; mStart++)
 | |
| 			{
 | |
| 				if (((block[mStart] & 0x0f) ^ 0x0a) == 0)
 | |
| 				{
 | |
| 					break;
 | |
| 				}
 | |
| 			}
 | |
| 
 | |
| 			mStart++;
 | |
| 
 | |
| 			//
 | |
| 			// check the hashes
 | |
| 			//
 | |
| 			byte[] hash = new byte[digest.GetDigestSize()];
 | |
| 
 | |
| 			int off = block.Length - delta - hash.Length;
 | |
| 
 | |
| 			//
 | |
| 			// there must be at least one byte of message string
 | |
| 			//
 | |
| 			if ((off - mStart) <= 0)
 | |
| 			{
 | |
| 				ClearBlock(mBuf);
 | |
| 				ClearBlock(block);
 | |
| 
 | |
| 				return false;
 | |
| 			}
 | |
| 
 | |
| 			//
 | |
| 			// if we contain the whole message as well, check the hash of that.
 | |
| 			//
 | |
| 			if ((block[0] & 0x20) == 0)
 | |
| 			{
 | |
| 				fullMessage = true;
 | |
| 
 | |
| 				digest.Reset();
 | |
| 				digest.BlockUpdate(block, mStart, off - mStart);
 | |
| 				digest.DoFinal(hash, 0);
 | |
| 
 | |
| 				for (int i = 0; i != hash.Length; i++)
 | |
| 				{
 | |
| 					block[off + i] ^= hash[i];
 | |
| 					if (block[off + i] != 0)
 | |
| 					{
 | |
| 						ClearBlock(mBuf);
 | |
| 						ClearBlock(block);
 | |
| 
 | |
| 						return false;
 | |
| 					}
 | |
| 				}
 | |
| 
 | |
| 				recoveredMessage = new byte[off - mStart];
 | |
| 				Array.Copy(block, mStart, recoveredMessage, 0, recoveredMessage.Length);
 | |
| 			}
 | |
| 			else
 | |
| 			{
 | |
| 				fullMessage = false;
 | |
| 
 | |
| 				digest.DoFinal(hash, 0);
 | |
| 
 | |
| 				for (int i = 0; i != hash.Length; i++)
 | |
| 				{
 | |
| 					block[off + i] ^= hash[i];
 | |
| 					if (block[off + i] != 0)
 | |
| 					{
 | |
| 						ClearBlock(mBuf);
 | |
| 						ClearBlock(block);
 | |
| 
 | |
| 						return false;
 | |
| 					}
 | |
| 				}
 | |
| 
 | |
| 				recoveredMessage = new byte[off - mStart];
 | |
| 				Array.Copy(block, mStart, recoveredMessage, 0, recoveredMessage.Length);
 | |
| 			}
 | |
| 
 | |
| 			//
 | |
| 			// if they've input a message check what we've recovered against
 | |
| 			// what was input.
 | |
| 			//
 | |
| 			if (messageLength != 0)
 | |
| 			{
 | |
| 				if (!IsSameAs(mBuf, recoveredMessage))
 | |
| 				{
 | |
| 					ClearBlock(mBuf);
 | |
| 					ClearBlock(block);
 | |
| 					ClearBlock(recoveredMessage);
 | |
| 
 | |
| 					return false;
 | |
| 				}
 | |
| 			}
 | |
| 
 | |
| 			ClearBlock(mBuf);
 | |
| 			ClearBlock(block);
 | |
| 
 | |
| 			return true;
 | |
| 		}
 | |
| 
 | |
| 		/// <summary>
 | |
| 		/// Return true if the full message was recoveredMessage.
 | |
| 		/// </summary>
 | |
| 		/// <returns> true on full message recovery, false otherwise.</returns>
 | |
| 		/// <seealso cref="ISignerWithRecovery.HasFullMessage"/>
 | |
| 		public virtual bool HasFullMessage()
 | |
| 		{
 | |
| 			return fullMessage;
 | |
| 		}
 | |
| 	}
 | |
| }
 |