using System; using Org.BouncyCastle.Crypto.Digests; using Org.BouncyCastle.Crypto.Parameters; using Org.BouncyCastle.Security; namespace Org.BouncyCastle.Crypto.Signers; public class PssSigner : ISigner { public const byte TrailerImplicit = 188; private readonly IDigest contentDigest1; private readonly IDigest contentDigest2; private readonly IDigest mgfDigest; private readonly IAsymmetricBlockCipher cipher; private SecureRandom random; private int hLen; private int mgfhLen; private int sLen; private bool sSet; private int emBits; private byte[] salt; private byte[] mDash; private byte[] block; private byte trailer; public virtual string AlgorithmName => mgfDigest.AlgorithmName + "withRSAandMGF1"; public static PssSigner CreateRawSigner(IAsymmetricBlockCipher cipher, IDigest digest) { return new PssSigner(cipher, new NullDigest(), digest, digest, digest.GetDigestSize(), null, 188); } public static PssSigner CreateRawSigner(IAsymmetricBlockCipher cipher, IDigest contentDigest, IDigest mgfDigest, int saltLen, byte trailer) { return new PssSigner(cipher, new NullDigest(), contentDigest, mgfDigest, saltLen, null, trailer); } public PssSigner(IAsymmetricBlockCipher cipher, IDigest digest) : this(cipher, digest, digest.GetDigestSize()) { } public PssSigner(IAsymmetricBlockCipher cipher, IDigest digest, int saltLen) : this(cipher, digest, saltLen, 188) { } public PssSigner(IAsymmetricBlockCipher cipher, IDigest digest, byte[] salt) : this(cipher, digest, digest, digest, salt.Length, salt, 188) { } public PssSigner(IAsymmetricBlockCipher cipher, IDigest contentDigest, IDigest mgfDigest, int saltLen) : this(cipher, contentDigest, mgfDigest, saltLen, 188) { } public PssSigner(IAsymmetricBlockCipher cipher, IDigest contentDigest, IDigest mgfDigest, byte[] salt) : this(cipher, contentDigest, contentDigest, mgfDigest, salt.Length, salt, 188) { } public PssSigner(IAsymmetricBlockCipher cipher, IDigest digest, int saltLen, byte trailer) : this(cipher, digest, digest, saltLen, 188) { } public PssSigner(IAsymmetricBlockCipher cipher, IDigest contentDigest, IDigest mgfDigest, int saltLen, byte trailer) : this(cipher, contentDigest, contentDigest, mgfDigest, saltLen, null, trailer) { } private PssSigner(IAsymmetricBlockCipher cipher, IDigest contentDigest1, IDigest contentDigest2, IDigest mgfDigest, int saltLen, byte[] salt, byte trailer) { this.cipher = cipher; this.contentDigest1 = contentDigest1; this.contentDigest2 = contentDigest2; this.mgfDigest = mgfDigest; hLen = contentDigest2.GetDigestSize(); mgfhLen = mgfDigest.GetDigestSize(); sLen = saltLen; sSet = salt != null; if (sSet) { this.salt = salt; } else { this.salt = new byte[saltLen]; } mDash = new byte[8 + saltLen + hLen]; this.trailer = trailer; } public virtual void Init(bool forSigning, ICipherParameters parameters) { if (parameters is ParametersWithRandom) { ParametersWithRandom parametersWithRandom = (ParametersWithRandom)parameters; parameters = parametersWithRandom.Parameters; random = parametersWithRandom.Random; } else if (forSigning) { random = new SecureRandom(); } cipher.Init(forSigning, parameters); RsaKeyParameters rsaKeyParameters = ((!(parameters is RsaBlindingParameters)) ? ((RsaKeyParameters)parameters) : ((RsaBlindingParameters)parameters).PublicKey); emBits = rsaKeyParameters.Modulus.BitLength - 1; if (emBits < 8 * hLen + 8 * sLen + 9) { throw new ArgumentException("key too small for specified hash and salt lengths"); } block = new byte[(emBits + 7) / 8]; } private void ClearBlock(byte[] block) { Array.Clear(block, 0, block.Length); } public virtual void Update(byte input) { contentDigest1.Update(input); } public virtual void BlockUpdate(byte[] input, int inOff, int length) { contentDigest1.BlockUpdate(input, inOff, length); } public virtual void Reset() { contentDigest1.Reset(); } public virtual byte[] GenerateSignature() { contentDigest1.DoFinal(mDash, mDash.Length - hLen - sLen); if (sLen != 0) { if (!sSet) { random.NextBytes(salt); } salt.CopyTo(mDash, mDash.Length - sLen); } byte[] array = new byte[hLen]; contentDigest2.BlockUpdate(mDash, 0, mDash.Length); contentDigest2.DoFinal(array, 0); block[block.Length - sLen - 1 - hLen - 1] = 1; salt.CopyTo(block, block.Length - sLen - hLen - 1); byte[] array2 = MaskGeneratorFunction1(array, 0, array.Length, block.Length - hLen - 1); byte[] array4; for (int i = 0; i != array2.Length; i++) { byte[] array3 = (array4 = block); int num = i; nint num2 = num; array3[num] = (byte)(array4[num2] ^ array2[i]); } (array4 = block)[0] = (byte)(array4[0] & (byte)(255 >> block.Length * 8 - emBits)); array.CopyTo(block, block.Length - hLen - 1); block[block.Length - 1] = trailer; byte[] result = cipher.ProcessBlock(block, 0, block.Length); ClearBlock(block); return result; } public virtual bool VerifySignature(byte[] signature) { contentDigest1.DoFinal(mDash, mDash.Length - hLen - sLen); byte[] array = cipher.ProcessBlock(signature, 0, signature.Length); array.CopyTo(block, block.Length - array.Length); if (block[block.Length - 1] != trailer) { ClearBlock(block); return false; } byte[] array2 = MaskGeneratorFunction1(block, block.Length - hLen - 1, hLen, block.Length - hLen - 1); byte[] array4; for (int i = 0; i != array2.Length; i++) { byte[] array3 = (array4 = block); int num = i; nint num2 = num; array3[num] = (byte)(array4[num2] ^ array2[i]); } (array4 = block)[0] = (byte)(array4[0] & (byte)(255 >> block.Length * 8 - emBits)); for (int j = 0; j != block.Length - hLen - sLen - 2; j++) { if (block[j] != 0) { ClearBlock(block); return false; } } if (block[block.Length - hLen - sLen - 2] != 1) { ClearBlock(block); return false; } if (sSet) { Array.Copy(salt, 0, mDash, mDash.Length - sLen, sLen); } else { Array.Copy(block, block.Length - sLen - hLen - 1, mDash, mDash.Length - sLen, sLen); } contentDigest2.BlockUpdate(mDash, 0, mDash.Length); contentDigest2.DoFinal(mDash, mDash.Length - hLen); int num3 = block.Length - hLen - 1; for (int k = mDash.Length - hLen; k != mDash.Length; k++) { if ((block[num3] ^ mDash[k]) != 0) { ClearBlock(mDash); ClearBlock(block); return false; } num3++; } ClearBlock(mDash); ClearBlock(block); return true; } 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 byte[] MaskGeneratorFunction1(byte[] Z, int zOff, int zLen, int length) { byte[] array = new byte[length]; byte[] array2 = new byte[mgfhLen]; byte[] array3 = new byte[4]; int i = 0; mgfDigest.Reset(); for (; i < length / mgfhLen; i++) { ItoOSP(i, array3); mgfDigest.BlockUpdate(Z, zOff, zLen); mgfDigest.BlockUpdate(array3, 0, array3.Length); mgfDigest.DoFinal(array2, 0); array2.CopyTo(array, i * mgfhLen); } if (i * mgfhLen < length) { ItoOSP(i, array3); mgfDigest.BlockUpdate(Z, zOff, zLen); mgfDigest.BlockUpdate(array3, 0, array3.Length); mgfDigest.DoFinal(array2, 0); Array.Copy(array2, 0, array, i * mgfhLen, array.Length - i * mgfhLen); } return array; } }