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;
}
}
}