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

Iso9796d2PssSigner

ISO9796-2 - mechanism using a hash function with recovery (scheme 2 and 3). Note: the usual length for the salt is the length of the hash function used in bytes.
using Org.BouncyCastle.Crypto.Parameters; using Org.BouncyCastle.Crypto.Utilities; using Org.BouncyCastle.Security; using Org.BouncyCastle.Utilities; using System; namespace Org.BouncyCastle.Crypto.Signers { public class Iso9796d2PssSigner : ISignerWithRecovery, ISigner { private IDigest digest; private IAsymmetricBlockCipher cipher; private SecureRandom random; private byte[] standardSalt; private int trailer; private int keyBits; private byte[] block; private byte[] mBuf; private int messageLength; private readonly int saltLength; private bool fullMessage; private byte[] recoveredMessage; private byte[] preSig; private byte[] preBlock; private int preMStart; private int preTLength; public virtual string AlgorithmName => digest.AlgorithmName + "withISO9796-2S2"; public byte[] GetRecoveredMessage() { return recoveredMessage; } public Iso9796d2PssSigner(IAsymmetricBlockCipher cipher, IDigest digest, int saltLength, bool isImplicit) { this.cipher = cipher; this.digest = digest; this.saltLength = saltLength; if (isImplicit) trailer = 188; else { if (IsoTrailers.NoTrailerAvailable(digest)) throw new ArgumentException("no valid trailer", "digest"); trailer = IsoTrailers.GetTrailer(digest); } } public Iso9796d2PssSigner(IAsymmetricBlockCipher cipher, IDigest digest, int saltLength) : this(cipher, digest, saltLength, false) { } public virtual void Init(bool forSigning, ICipherParameters parameters) { ParametersWithRandom parametersWithRandom = parameters as ParametersWithRandom; RsaKeyParameters rsaKeyParameters; if (parametersWithRandom != null) { rsaKeyParameters = (RsaKeyParameters)parametersWithRandom.Parameters; random = (forSigning ? parametersWithRandom.Random : null); } else { ParametersWithSalt parametersWithSalt = parameters as ParametersWithSalt; if (parametersWithSalt != null) { if (!forSigning) throw new ArgumentException("ParametersWithSalt only valid for signing", "parameters"); rsaKeyParameters = (RsaKeyParameters)parametersWithSalt.Parameters; standardSalt = parametersWithSalt.GetSalt(); if (standardSalt.Length != saltLength) throw new ArgumentException("Fixed salt is of wrong length"); } else { rsaKeyParameters = (RsaKeyParameters)parameters; random = (forSigning ? CryptoServicesRegistrar.GetSecureRandom() : null); } } cipher.Init(forSigning, rsaKeyParameters); keyBits = rsaKeyParameters.Modulus.BitLength; block = new byte[(keyBits + 7) / 8]; int num = (trailer == 188) ? 1 : 2; mBuf = new byte[block.Length - digest.GetDigestSize() - saltLength - 1 - num]; Reset(); } private bool IsSameAs(byte[] a, byte[] b) { if (messageLength == b.Length) return Arrays.FixedTimeEquals(messageLength, a, 0, b, 0); return false; } 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.Length < (keyBits + 7) / 8) { byte[] array2 = new byte[(keyBits + 7) / 8]; Array.Copy(array, 0, array2, array2.Length - array.Length, array.Length); ClearBlock(array); array = array2; } int num; if (array[array.Length - 1] == 188) num = 1; else { int num2 = Pack.BE_To_UInt16(array, array.Length - 2); 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 digestSize = digest.GetDigestSize(); int num3 = array.Length - digestSize - num; byte[] array3 = MaskGeneratorFunction1(array, num3, digestSize, num3); Bytes.XorTo(num3, array3, array); array[0] &= 127; int num4 = 0; while (num4 < array.Length && array[num4++] != 1) { } if (num4 >= array.Length) ClearBlock(array); fullMessage = (num4 > 1); recoveredMessage = new byte[array3.Length - num4 - saltLength]; Array.Copy(array, num4, recoveredMessage, 0, recoveredMessage.Length); recoveredMessage.CopyTo(mBuf, 0); preSig = signature; preBlock = array; preMStart = num4; preTLength = num; } public virtual void Update(byte input) { if (preSig == null && messageLength < mBuf.Length) mBuf[messageLength++] = input; else digest.Update(input); } public virtual void BlockUpdate(byte[] input, int inOff, int inLen) { if (preSig == null) { while (inLen > 0 && messageLength < mBuf.Length) { Update(input[inOff]); inOff++; inLen--; } } if (inLen > 0) digest.BlockUpdate(input, inOff, inLen); } public virtual int GetMaxSignatureSize() { return cipher.GetOutputBlockSize(); } public virtual byte[] GenerateSignature() { byte[] array = standardSalt ?? SecureRandom.GetNextBytes(random, saltLength); int digestSize = digest.GetDigestSize(); byte[] array2 = new byte[digestSize]; digest.DoFinal(array2, 0); byte[] array3 = new byte[8]; Pack.UInt64_To_BE((uint)(messageLength * 8), array3); digest.BlockUpdate(array3, 0, array3.Length); if (messageLength > 0) digest.BlockUpdate(mBuf, 0, messageLength); digest.BlockUpdate(array2, 0, array2.Length); digest.BlockUpdate(array, 0, array.Length); digest.DoFinal(array2, 0); int num = 2; if (trailer == 188) num = 1; int num2 = block.Length - messageLength - array.Length - digestSize - num - 1; block[num2] = 1; Array.Copy(mBuf, 0, block, num2 + 1, messageLength); Array.Copy(array, 0, block, num2 + 1 + messageLength, array.Length); int num3 = block.Length - digestSize - num; byte[] x = MaskGeneratorFunction1(array2, 0, array2.Length, num3); Bytes.XorTo(num3, x, block); Array.Copy(array2, 0, block, block.Length - digestSize - num, digestSize); if (trailer == 188) block[block.Length - 1] = 188; else Pack.UInt16_To_BE((ushort)trailer, block, block.Length - 2); block[0] &= 127; byte[] result = cipher.ProcessBlock(block, 0, block.Length); ClearBlock(mBuf); ClearBlock(block); messageLength = 0; return result; } public virtual bool VerifySignature(byte[] signature) { int digestSize = digest.GetDigestSize(); byte[] array = new byte[digestSize]; digest.DoFinal(array, 0); if (preSig == null) try { UpdateWithRecoveredMessage(signature); } catch (Exception) { return false; } else if (!Arrays.AreEqual(preSig, signature)) { throw new InvalidOperationException("UpdateWithRecoveredMessage called on different signature"); } byte[] array2 = preBlock; int num = preMStart; int num2 = preTLength; preSig = null; preBlock = null; byte[] array3 = new byte[8]; Pack.UInt64_To_BE((uint)(recoveredMessage.Length * 8), array3); digest.BlockUpdate(array3, 0, array3.Length); if (recoveredMessage.Length != 0) digest.BlockUpdate(recoveredMessage, 0, recoveredMessage.Length); digest.BlockUpdate(array, 0, array.Length); if (standardSalt != null) digest.BlockUpdate(standardSalt, 0, standardSalt.Length); else digest.BlockUpdate(array2, num + recoveredMessage.Length, saltLength); digest.DoFinal(array, 0); bool num3 = Arrays.FixedTimeEquals(digestSize, array, 0, array2, array2.Length - num2 - digestSize); ClearBlock(array2); ClearBlock(array); if (!num3) { fullMessage = false; messageLength = 0; ClearBlock(recoveredMessage); return false; } bool result = messageLength == 0 || IsSameAs(mBuf, recoveredMessage); messageLength = 0; ClearBlock(mBuf); return result; } public virtual void Reset() { digest.Reset(); messageLength = 0; if (mBuf != null) ClearBlock(mBuf); if (recoveredMessage != null) { ClearBlock(recoveredMessage); recoveredMessage = null; } fullMessage = false; if (preSig != null) { preSig = null; ClearBlock(preBlock); preBlock = null; } } public virtual bool HasFullMessage() { return fullMessage; } private byte[] MaskGeneratorFunction1(byte[] z, int zOff, int zLen, int length) { int digestSize = digest.GetDigestSize(); byte[] array = new byte[length]; byte[] array2 = new byte[System.Math.Max(4, digestSize)]; uint num = 0; digest.Reset(); int i = 0; for (int num2 = length - digestSize; i <= num2; i += digestSize) { Pack.UInt32_To_BE(num++, array2); digest.BlockUpdate(z, zOff, zLen); digest.BlockUpdate(array2, 0, 4); digest.DoFinal(array, i); } if (i < length) { Pack.UInt32_To_BE(num, array2); digest.BlockUpdate(z, zOff, zLen); digest.BlockUpdate(array2, 0, 4); digest.DoFinal(array2, 0); Array.Copy(array2, 0, array, i, length - i); } return array; } } }