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