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

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.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 hLen; 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; hLen = digest.GetDigestSize(); 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]; if (trailer == 188) mBuf = new byte[block.Length - digest.GetDigestSize() - saltLength - 1 - 1]; else mBuf = new byte[block.Length - digest.GetDigestSize() - saltLength - 1 - 2]; Reset(); } private bool IsSameAs(byte[] a, byte[] b) { if (messageLength != b.Length) return false; bool result = true; for (int i = 0; i != b.Length; 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.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] & 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; } byte[] output = new byte[hLen]; digest.DoFinal(output, 0); byte[] array3 = MaskGeneratorFunction1(array, array.Length - hLen - num, hLen, array.Length - hLen - num); for (int i = 0; i != array3.Length; i++) { array[i] ^= array3[i]; } array[0] &= 127; int num3 = 0; while (num3 < array.Length && array[num3++] != 1) { } if (num3 >= array.Length) ClearBlock(array); fullMessage = (num3 > 1); recoveredMessage = new byte[array3.Length - num3 - saltLength]; Array.Copy(array, num3, recoveredMessage, 0, recoveredMessage.Length); recoveredMessage.CopyTo(mBuf, 0); preSig = signature; preBlock = array; preMStart = num3; 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 = new byte[digest.GetDigestSize()]; digest.DoFinal(array, 0); byte[] array2 = new byte[8]; LtoOSP(messageLength * 8, array2); digest.BlockUpdate(array2, 0, array2.Length); digest.BlockUpdate(mBuf, 0, messageLength); digest.BlockUpdate(array, 0, array.Length); byte[] array3; if (standardSalt != null) array3 = standardSalt; else { array3 = new byte[saltLength]; random.NextBytes(array3); } digest.BlockUpdate(array3, 0, array3.Length); byte[] array4 = new byte[digest.GetDigestSize()]; digest.DoFinal(array4, 0); int num = 2; if (trailer == 188) num = 1; int num2 = block.Length - messageLength - array3.Length - hLen - num - 1; block[num2] = 1; Array.Copy(mBuf, 0, block, num2 + 1, messageLength); Array.Copy(array3, 0, block, num2 + 1 + messageLength, array3.Length); byte[] array5 = MaskGeneratorFunction1(array4, 0, array4.Length, block.Length - hLen - num); for (int i = 0; i != array5.Length; i++) { block[i] ^= array5[i]; } Array.Copy(array4, 0, block, block.Length - hLen - num, hLen); if (trailer == 188) block[block.Length - 1] = 188; else { block[block.Length - 2] = (byte)((uint)trailer >> 8); block[block.Length - 1] = (byte)trailer; } 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) { byte[] array = new byte[hLen]; digest.DoFinal(array, 0); int num = 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; num = preMStart; int num2 = preTLength; preSig = null; preBlock = null; byte[] array3 = new byte[8]; LtoOSP(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); byte[] array4 = new byte[digest.GetDigestSize()]; digest.DoFinal(array4, 0); int num3 = array2.Length - num2 - array4.Length; bool flag = true; for (int i = 0; i != array4.Length; i++) { if (array4[i] != array2[num3 + i]) flag = false; } ClearBlock(array2); ClearBlock(array4); if (!flag) { fullMessage = false; messageLength = 0; ClearBlock(recoveredMessage); return false; } if (messageLength != 0 && !IsSameAs(mBuf, recoveredMessage)) { messageLength = 0; ClearBlock(mBuf); return false; } messageLength = 0; ClearBlock(mBuf); return true; } 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 void ItoOSP(int i, byte[] sp) { sp[0] = (byte)((uint)i >> 24); sp[1] = (byte)((uint)i >> 16); sp[2] = (byte)((uint)i >> 8); sp[3] = (byte)i; } private void LtoOSP(long l, byte[] sp) { sp[0] = (byte)((ulong)l >> 56); sp[1] = (byte)((ulong)l >> 48); sp[2] = (byte)((ulong)l >> 40); sp[3] = (byte)((ulong)l >> 32); sp[4] = (byte)((ulong)l >> 24); sp[5] = (byte)((ulong)l >> 16); sp[6] = (byte)((ulong)l >> 8); sp[7] = (byte)l; } private byte[] MaskGeneratorFunction1(byte[] Z, int zOff, int zLen, int length) { byte[] array = new byte[length]; byte[] array2 = new byte[hLen]; byte[] array3 = new byte[4]; int num = 0; digest.Reset(); do { ItoOSP(num, array3); digest.BlockUpdate(Z, zOff, zLen); digest.BlockUpdate(array3, 0, array3.Length); digest.DoFinal(array2, 0); Array.Copy(array2, 0, array, num * hLen, hLen); } while (++num < length / hLen); if (num * hLen < length) { ItoOSP(num, array3); digest.BlockUpdate(Z, zOff, zLen); digest.BlockUpdate(array3, 0, array3.Length); digest.DoFinal(array2, 0); Array.Copy(array2, 0, array, num * hLen, array.Length - num * hLen); } return array; } } }