<PackageReference Include="BouncyCastle.Cryptography" Version="2.6.2" />

Iso9796d2Signer

ISO9796-2 - mechanism using a hash function with recovery (scheme 1)
using Org.BouncyCastle.Crypto.Parameters; using Org.BouncyCastle.Utilities; using System; namespace Org.BouncyCastle.Crypto.Signers { public class Iso9796d2Signer : ISignerWithRecovery, ISigner { 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; private byte[] preSig; private byte[] preBlock; public virtual string AlgorithmName => digest.AlgorithmName + "withISO9796-2S1"; public byte[] GetRecoveredMessage() { return recoveredMessage; } public Iso9796d2Signer(IAsymmetricBlockCipher cipher, IDigest digest, bool isImplicit) { this.cipher = cipher; this.digest = digest; if (isImplicit) trailer = 188; else { if (IsoTrailers.NoTrailerAvailable(digest)) throw new ArgumentException("no valid trailer", "digest"); trailer = IsoTrailers.GetTrailer(digest); } } public Iso9796d2Signer(IAsymmetricBlockCipher cipher, IDigest digest) : this(cipher, digest, false) { } public virtual void Init(bool forSigning, ICipherParameters parameters) { RsaKeyParameters rsaKeyParameters = (RsaKeyParameters)parameters; cipher.Init(forSigning, rsaKeyParameters); keyBits = rsaKeyParameters.Modulus.BitLength; block = new byte[(keyBits + 7) / 8]; if (trailer == 188) mBuf = new byte[block.Length - digest.GetDigestSize() - 2]; else mBuf = new byte[block.Length - digest.GetDigestSize() - 3]; Reset(); } private bool IsSameAs(byte[] a, byte[] b) { int num; if (messageLength > mBuf.Length) { if (mBuf.Length > b.Length) return false; num = mBuf.Length; } else { if (messageLength != b.Length) return false; num = b.Length; } bool result = true; for (int i = 0; i != num; i++) { if (a[i] != b[i]) result = false; } return result; } private void ClearBlock(byte[] block) { Array.Clear(block, 0, block.Length); } public virtual void UpdateWithRecoveredMessage(byte[] signature) { byte[] array = cipher.ProcessBlock(signature, 0, signature.Length); if (((array[0] & 192) ^ 64) != 0) throw new InvalidCipherTextException("malformed signature"); if (((array[array.Length - 1] & 15) ^ 12) != 0) throw new InvalidCipherTextException("malformed signature"); int num = 0; if (((array[array.Length - 1] & 255) ^ 188) == 0) num = 1; else { int num2 = ((array[array.Length - 2] & 255) << 8) | (array[array.Length - 1] & 255); if (IsoTrailers.NoTrailerAvailable(digest)) throw new ArgumentException("unrecognised hash in signature"); if (num2 != IsoTrailers.GetTrailer(digest)) throw new InvalidOperationException("signer initialised with wrong digest for trailer " + num2.ToString()); num = 2; } int num3 = 0; for (num3 = 0; num3 != array.Length && ((array[num3] & 15) ^ 10) != 0; num3++) { } num3++; int num4 = array.Length - num - digest.GetDigestSize(); if (num4 - num3 <= 0) throw new InvalidCipherTextException("malformed block"); if ((array[0] & 32) == 0) { fullMessage = true; recoveredMessage = new byte[num4 - num3]; Array.Copy(array, num3, recoveredMessage, 0, recoveredMessage.Length); } else { fullMessage = false; recoveredMessage = new byte[num4 - num3]; Array.Copy(array, num3, recoveredMessage, 0, recoveredMessage.Length); } preSig = signature; preBlock = array; digest.BlockUpdate(recoveredMessage, 0, recoveredMessage.Length); messageLength = recoveredMessage.Length; recoveredMessage.CopyTo(mBuf, 0); } public virtual void Update(byte input) { digest.Update(input); if (messageLength < mBuf.Length) mBuf[messageLength] = input; messageLength++; } public virtual void BlockUpdate(byte[] input, int inOff, int inLen) { while (inLen > 0 && messageLength < mBuf.Length) { Update(input[inOff]); inOff++; inLen--; } if (inLen > 0) { digest.BlockUpdate(input, inOff, inLen); messageLength += inLen; } } public virtual int GetMaxSignatureSize() { return cipher.GetOutputBlockSize(); } public virtual byte[] GenerateSignature() { int digestSize = digest.GetDigestSize(); int num = 0; int num2 = 0; if (trailer == 188) { num = 8; num2 = block.Length - digestSize - 1; digest.DoFinal(block, num2); block[block.Length - 1] = 188; } else { num = 16; num2 = block.Length - digestSize - 2; digest.DoFinal(block, num2); block[block.Length - 2] = (byte)((uint)trailer >> 8); block[block.Length - 1] = (byte)trailer; } byte b = 0; int num3 = (digestSize + messageLength) * 8 + num + 4 - keyBits; if (num3 > 0) { int num4 = messageLength - (num3 + 7) / 8; b = 96; num2 -= num4; Array.Copy(mBuf, 0, block, num2, num4); } else { b = 64; num2 -= messageLength; Array.Copy(mBuf, 0, block, num2, messageLength); } if (num2 - 1 > 0) { for (int num5 = num2 - 1; num5 != 0; num5--) { block[num5] = 187; } block[num2 - 1] ^= 1; block[0] = 11; block[0] |= b; } else { block[0] = 10; block[0] |= b; } byte[] result = cipher.ProcessBlock(block, 0, block.Length); messageLength = 0; ClearBlock(mBuf); ClearBlock(block); return result; } public virtual bool VerifySignature(byte[] signature) { byte[] array; if (preSig == null) try { array = cipher.ProcessBlock(signature, 0, signature.Length); } catch (Exception) { return false; } else { if (!Arrays.AreEqual(preSig, signature)) throw new InvalidOperationException("updateWithRecoveredMessage called on different signature"); array = preBlock; preSig = null; preBlock = null; } if (((array[0] & 192) ^ 64) != 0) return ReturnFalse(array); if (((array[array.Length - 1] & 15) ^ 12) != 0) return ReturnFalse(array); int num = 0; if (((array[array.Length - 1] & 255) ^ 188) == 0) num = 1; else { int num2 = ((array[array.Length - 2] & 255) << 8) | (array[array.Length - 1] & 255); if (IsoTrailers.NoTrailerAvailable(digest)) throw new ArgumentException("unrecognised hash in signature"); if (num2 != IsoTrailers.GetTrailer(digest)) throw new InvalidOperationException("signer initialised with wrong digest for trailer " + num2.ToString()); num = 2; } int i; for (i = 0; i != array.Length && ((array[i] & 15) ^ 10) != 0; i++) { } i++; byte[] array2 = new byte[digest.GetDigestSize()]; int num3 = array.Length - num - array2.Length; if (num3 - i <= 0) return ReturnFalse(array); if ((array[0] & 32) == 0) { fullMessage = true; if (messageLength > num3 - i) return ReturnFalse(array); digest.Reset(); digest.BlockUpdate(array, i, num3 - i); digest.DoFinal(array2, 0); bool flag = true; for (int j = 0; j != array2.Length; j++) { array[num3 + j] ^= array2[j]; if (array[num3 + j] != 0) flag = false; } if (!flag) return ReturnFalse(array); recoveredMessage = new byte[num3 - i]; Array.Copy(array, i, recoveredMessage, 0, recoveredMessage.Length); } else { fullMessage = false; digest.DoFinal(array2, 0); bool flag2 = true; for (int k = 0; k != array2.Length; k++) { array[num3 + k] ^= array2[k]; if (array[num3 + k] != 0) flag2 = false; } if (!flag2) return ReturnFalse(array); recoveredMessage = new byte[num3 - i]; Array.Copy(array, i, recoveredMessage, 0, recoveredMessage.Length); } if (messageLength != 0 && !IsSameAs(mBuf, recoveredMessage)) return ReturnFalse(array); ClearBlock(mBuf); ClearBlock(array); messageLength = 0; return true; } public virtual void Reset() { digest.Reset(); messageLength = 0; ClearBlock(mBuf); if (recoveredMessage != null) ClearBlock(recoveredMessage); recoveredMessage = null; fullMessage = false; if (preSig != null) { preSig = null; ClearBlock(preBlock); preBlock = null; } } private bool ReturnFalse(byte[] block) { messageLength = 0; ClearBlock(mBuf); ClearBlock(block); return false; } public virtual bool HasFullMessage() { return fullMessage; } } }