434 lines
9.2 KiB
C#
434 lines
9.2 KiB
C#
using System;
|
|
using Org.BouncyCastle.Crypto.Parameters;
|
|
using Org.BouncyCastle.Utilities;
|
|
|
|
namespace Org.BouncyCastle.Crypto.Signers;
|
|
|
|
public class Iso9796d2Signer : ISignerWithRecovery, ISigner
|
|
{
|
|
[Obsolete("Use 'IsoTrailers' instead")]
|
|
public const int TrailerImplicit = 188;
|
|
|
|
[Obsolete("Use 'IsoTrailers' instead")]
|
|
public const int TrailerRipeMD160 = 12748;
|
|
|
|
[Obsolete("Use 'IsoTrailers' instead")]
|
|
public const int TrailerRipeMD128 = 13004;
|
|
|
|
[Obsolete("Use 'IsoTrailers' instead")]
|
|
public const int TrailerSha1 = 13260;
|
|
|
|
[Obsolete("Use 'IsoTrailers' instead")]
|
|
public const int TrailerSha256 = 13516;
|
|
|
|
[Obsolete("Use 'IsoTrailers' instead")]
|
|
public const int TrailerSha512 = 13772;
|
|
|
|
[Obsolete("Use 'IsoTrailers' instead")]
|
|
public const int TrailerSha384 = 14028;
|
|
|
|
[Obsolete("Use 'IsoTrailers' instead")]
|
|
public const int TrailerWhirlpool = 14284;
|
|
|
|
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;
|
|
return;
|
|
}
|
|
if (IsoTrailers.NoTrailerAvailable(digest))
|
|
{
|
|
throw new ArgumentException("no valid trailer", "digest");
|
|
}
|
|
trailer = IsoTrailers.GetTrailer(digest);
|
|
}
|
|
|
|
public Iso9796d2Signer(IAsymmetricBlockCipher cipher, IDigest digest)
|
|
: this(cipher, digest, isImplicit: 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] & 0xC0) ^ 0x40) != 0)
|
|
{
|
|
throw new InvalidCipherTextException("malformed signature");
|
|
}
|
|
if (((array[^1] & 0xF) ^ 0xC) != 0)
|
|
{
|
|
throw new InvalidCipherTextException("malformed signature");
|
|
}
|
|
int num = 0;
|
|
if (((array[^1] & 0xFF) ^ 0xBC) == 0)
|
|
{
|
|
num = 1;
|
|
}
|
|
else
|
|
{
|
|
int num2 = ((array[^2] & 0xFF) << 8) | (array[^1] & 0xFF);
|
|
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);
|
|
}
|
|
num = 2;
|
|
}
|
|
int num3 = 0;
|
|
for (num3 = 0; num3 != array.Length && ((array[num3] & 0xF) ^ 0xA) != 0; num3++)
|
|
{
|
|
}
|
|
num3++;
|
|
int num4 = array.Length - num - digest.GetDigestSize();
|
|
if (num4 - num3 <= 0)
|
|
{
|
|
throw new InvalidCipherTextException("malformed block");
|
|
}
|
|
if ((array[0] & 0x20) == 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 length)
|
|
{
|
|
while (length > 0 && messageLength < mBuf.Length)
|
|
{
|
|
Update(input[inOff]);
|
|
inOff++;
|
|
length--;
|
|
}
|
|
digest.BlockUpdate(input, inOff, length);
|
|
messageLength += length;
|
|
}
|
|
|
|
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;
|
|
}
|
|
}
|
|
|
|
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;
|
|
}
|
|
byte[] array2;
|
|
byte[] array = (array2 = block);
|
|
int num6 = num2 - 1;
|
|
nint num7 = num6;
|
|
array[num6] = (byte)(array2[num7] ^ 1);
|
|
block[0] = 11;
|
|
(array2 = block)[0] = (byte)(array2[0] | b);
|
|
}
|
|
else
|
|
{
|
|
block[0] = 10;
|
|
byte[] array2;
|
|
(array2 = block)[0] = (byte)(array2[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] & 0xC0) ^ 0x40) != 0)
|
|
{
|
|
return ReturnFalse(array);
|
|
}
|
|
if (((array[^1] & 0xF) ^ 0xC) != 0)
|
|
{
|
|
return ReturnFalse(array);
|
|
}
|
|
int num = 0;
|
|
if (((array[^1] & 0xFF) ^ 0xBC) == 0)
|
|
{
|
|
num = 1;
|
|
}
|
|
else
|
|
{
|
|
int num2 = ((array[^2] & 0xFF) << 8) | (array[^1] & 0xFF);
|
|
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);
|
|
}
|
|
num = 2;
|
|
}
|
|
int i;
|
|
for (i = 0; i != array.Length && ((array[i] & 0xF) ^ 0xA) != 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] & 0x20) == 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++)
|
|
{
|
|
byte[] array4;
|
|
byte[] array3 = (array4 = array);
|
|
int num4 = num3 + j;
|
|
nint num5 = num4;
|
|
array3[num4] = (byte)(array4[num5] ^ 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++)
|
|
{
|
|
byte[] array4;
|
|
byte[] array5 = (array4 = array);
|
|
int num6 = num3 + k;
|
|
nint num5 = num6;
|
|
array5[num6] = (byte)(array4[num5] ^ 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;
|
|
}
|
|
|
|
private bool ReturnFalse(byte[] block)
|
|
{
|
|
messageLength = 0;
|
|
ClearBlock(mBuf);
|
|
ClearBlock(block);
|
|
return false;
|
|
}
|
|
|
|
public virtual bool HasFullMessage()
|
|
{
|
|
return fullMessage;
|
|
}
|
|
}
|