上海虹口龙之梦项目
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 

623 lines
18 KiB

#if !BESTHTTP_DISABLE_ALTERNATE_SSL && (!UNITY_WEBGL || UNITY_EDITOR)
#pragma warning disable
using System;
using System.Collections;
using BestHTTP.SecureProtocol.Org.BouncyCastle.Crypto.Digests;
using BestHTTP.SecureProtocol.Org.BouncyCastle.Crypto.Parameters;
using BestHTTP.SecureProtocol.Org.BouncyCastle.Security;
using BestHTTP.SecureProtocol.Org.BouncyCastle.Utilities;
namespace BestHTTP.SecureProtocol.Org.BouncyCastle.Crypto.Signers
{
/// <summary> ISO9796-2 - mechanism using a hash function with recovery (scheme 2 and 3).
/// <p>
/// Note: the usual length for the salt is the length of the hash
/// function used in bytes.</p>
/// </summary>
public class Iso9796d2PssSigner
: ISignerWithRecovery
{
/// <summary>
/// Return a reference to the recoveredMessage message.
/// </summary>
/// <returns>The full/partial recoveredMessage message.</returns>
/// <seealso cref="ISignerWithRecovery.GetRecoveredMessage"/>
public byte[] GetRecoveredMessage()
{
return recoveredMessage;
}
public const int TrailerImplicit = 0xBC;
public const int TrailerRipeMD160 = 0x31CC;
public const int TrailerRipeMD128 = 0x32CC;
public const int TrailerSha1 = 0x33CC;
public const int TrailerSha256 = 0x34CC;
public const int TrailerSha512 = 0x35CC;
public const int TrailerSha384 = 0x36CC;
public const int TrailerWhirlpool = 0x37CC;
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;
/// <summary>
/// Generate a signer with either implicit or explicit trailers for ISO9796-2, scheme 2 or 3.
/// </summary>
/// <param name="cipher">base cipher to use for signature creation/verification</param>
/// <param name="digest">digest to use.</param>
/// <param name="saltLength">length of salt in bytes.</param>
/// <param name="isImplicit">whether or not the trailer is implicit or gives the hash.</param>
public Iso9796d2PssSigner(
IAsymmetricBlockCipher cipher,
IDigest digest,
int saltLength,
bool isImplicit)
{
this.cipher = cipher;
this.digest = digest;
this.hLen = digest.GetDigestSize();
this.saltLength = saltLength;
if (isImplicit)
{
trailer = IsoTrailers.TRAILER_IMPLICIT;
}
else if (IsoTrailers.NoTrailerAvailable(digest))
{
throw new ArgumentException("no valid trailer", "digest");
}
else
{
trailer = IsoTrailers.GetTrailer(digest);
}
}
/// <summary> Constructor for a signer with an explicit digest trailer.
///
/// </summary>
/// <param name="cipher">cipher to use.
/// </param>
/// <param name="digest">digest to sign with.
/// </param>
/// <param name="saltLength">length of salt in bytes.
/// </param>
public Iso9796d2PssSigner(
IAsymmetricBlockCipher cipher,
IDigest digest,
int saltLength)
: this(cipher, digest, saltLength, false)
{
}
public virtual string AlgorithmName
{
get { return digest.AlgorithmName + "with" + "ISO9796-2S2"; }
}
/// <summary>Initialise the signer.</summary>
/// <param name="forSigning">true if for signing, false if for verification.</param>
/// <param name="parameters">parameters for signature generation/verification. If the
/// parameters are for generation they should be a ParametersWithRandom,
/// a ParametersWithSalt, or just an RsaKeyParameters object. If RsaKeyParameters
/// are passed in a SecureRandom will be created.
/// </param>
/// <exception cref="ArgumentException">if wrong parameter type or a fixed
/// salt is passed in which is the wrong length.
/// </exception>
public virtual void Init(
bool forSigning,
ICipherParameters parameters)
{
RsaKeyParameters kParam;
if (parameters is ParametersWithRandom)
{
ParametersWithRandom p = (ParametersWithRandom) parameters;
kParam = (RsaKeyParameters) p.Parameters;
if (forSigning)
{
random = p.Random;
}
}
else if (parameters is ParametersWithSalt)
{
if (!forSigning)
throw new ArgumentException("ParametersWithSalt only valid for signing", "parameters");
ParametersWithSalt p = (ParametersWithSalt) parameters;
kParam = (RsaKeyParameters) p.Parameters;
standardSalt = p.GetSalt();
if (standardSalt.Length != saltLength)
throw new ArgumentException("Fixed salt is of wrong length");
}
else
{
kParam = (RsaKeyParameters) parameters;
if (forSigning)
{
random = new SecureRandom();
}
}
cipher.Init(forSigning, kParam);
keyBits = kParam.Modulus.BitLength;
block = new byte[(keyBits + 7) / 8];
if (trailer == IsoTrailers.TRAILER_IMPLICIT)
{
mBuf = new byte[block.Length - digest.GetDigestSize() - saltLength - 1 - 1];
}
else
{
mBuf = new byte[block.Length - digest.GetDigestSize() - saltLength - 1 - 2];
}
Reset();
}
/// <summary> compare two byte arrays - constant time.</summary>
private bool IsSameAs(byte[] a, byte[] b)
{
if (messageLength != b.Length)
{
return false;
}
bool isOkay = true;
for (int i = 0; i != b.Length; i++)
{
if (a[i] != b[i])
{
isOkay = false;
}
}
return isOkay;
}
/// <summary> clear possible sensitive data</summary>
private void ClearBlock(
byte[] block)
{
Array.Clear(block, 0, block.Length);
}
public virtual void UpdateWithRecoveredMessage(
byte[] signature)
{
byte[] block = cipher.ProcessBlock(signature, 0, signature.Length);
//
// adjust block size for leading zeroes if necessary
//
if (block.Length < (keyBits + 7) / 8)
{
byte[] tmp = new byte[(keyBits + 7) / 8];
Array.Copy(block, 0, tmp, tmp.Length - block.Length, block.Length);
ClearBlock(block);
block = tmp;
}
int tLength;
if (((block[block.Length - 1] & 0xFF) ^ 0xBC) == 0)
{
tLength = 1;
}
else
{
int sigTrail = ((block[block.Length - 2] & 0xFF) << 8) | (block[block.Length - 1] & 0xFF);
if (IsoTrailers.NoTrailerAvailable(digest))
throw new ArgumentException("unrecognised hash in signature");
if (sigTrail != IsoTrailers.GetTrailer(digest))
throw new InvalidOperationException("signer initialised with wrong digest for trailer " + sigTrail);
tLength = 2;
}
//
// calculate H(m2)
//
byte[] m2Hash = new byte[hLen];
digest.DoFinal(m2Hash, 0);
//
// remove the mask
//
byte[] dbMask = MaskGeneratorFunction1(block, block.Length - hLen - tLength, hLen, block.Length - hLen - tLength);
for (int i = 0; i != dbMask.Length; i++)
{
block[i] ^= dbMask[i];
}
block[0] &= 0x7f;
//
// find out how much padding we've got
//
int mStart = 0;
while (mStart < block.Length)
{
if (block[mStart++] == 0x01)
break;
}
if (mStart >= block.Length)
{
ClearBlock(block);
}
fullMessage = (mStart > 1);
recoveredMessage = new byte[dbMask.Length - mStart - saltLength];
Array.Copy(block, mStart, recoveredMessage, 0, recoveredMessage.Length);
recoveredMessage.CopyTo(mBuf, 0);
preSig = signature;
preBlock = block;
preMStart = mStart;
preTLength = tLength;
}
/// <summary> update the internal digest with the byte b</summary>
public virtual void Update(
byte input)
{
if (preSig == null && messageLength < mBuf.Length)
{
mBuf[messageLength++] = input;
}
else
{
digest.Update(input);
}
}
/// <summary> update the internal digest with the byte array in</summary>
public virtual void BlockUpdate(
byte[] input,
int inOff,
int length)
{
if (preSig == null)
{
while (length > 0 && messageLength < mBuf.Length)
{
this.Update(input[inOff]);
inOff++;
length--;
}
}
if (length > 0)
{
digest.BlockUpdate(input, inOff, length);
}
}
/// <summary> reset the internal state</summary>
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;
}
}
/// <summary> Generate a signature for the loaded message using the key we were
/// initialised with.
/// </summary>
public virtual byte[] GenerateSignature()
{
int digSize = digest.GetDigestSize();
byte[] m2Hash = new byte[digSize];
digest.DoFinal(m2Hash, 0);
byte[] C = new byte[8];
LtoOSP(messageLength * 8, C);
digest.BlockUpdate(C, 0, C.Length);
digest.BlockUpdate(mBuf, 0, messageLength);
digest.BlockUpdate(m2Hash, 0, m2Hash.Length);
byte[] salt;
if (standardSalt != null)
{
salt = standardSalt;
}
else
{
salt = new byte[saltLength];
random.NextBytes(salt);
}
digest.BlockUpdate(salt, 0, salt.Length);
byte[] hash = new byte[digest.GetDigestSize()];
digest.DoFinal(hash, 0);
int tLength = 2;
if (trailer == IsoTrailers.TRAILER_IMPLICIT)
{
tLength = 1;
}
int off = block.Length - messageLength - salt.Length - hLen - tLength - 1;
block[off] = (byte) (0x01);
Array.Copy(mBuf, 0, block, off + 1, messageLength);
Array.Copy(salt, 0, block, off + 1 + messageLength, salt.Length);
byte[] dbMask = MaskGeneratorFunction1(hash, 0, hash.Length, block.Length - hLen - tLength);
for (int i = 0; i != dbMask.Length; i++)
{
block[i] ^= dbMask[i];
}
Array.Copy(hash, 0, block, block.Length - hLen - tLength, hLen);
if (trailer == IsoTrailers.TRAILER_IMPLICIT)
{
block[block.Length - 1] = (byte)IsoTrailers.TRAILER_IMPLICIT;
}
else
{
block[block.Length - 2] = (byte) ((uint)trailer >> 8);
block[block.Length - 1] = (byte) trailer;
}
block[0] &= (byte) (0x7f);
byte[] b = cipher.ProcessBlock(block, 0, block.Length);
ClearBlock(mBuf);
ClearBlock(block);
messageLength = 0;
return b;
}
/// <summary> return true if the signature represents a ISO9796-2 signature
/// for the passed in message.
/// </summary>
public virtual bool VerifySignature(
byte[] signature)
{
//
// calculate H(m2)
//
byte[] m2Hash = new byte[hLen];
digest.DoFinal(m2Hash, 0);
byte[] block;
int tLength;
int mStart = 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");
}
}
block = preBlock;
mStart = preMStart;
tLength = preTLength;
preSig = null;
preBlock = null;
//
// check the hashes
//
byte[] C = new byte[8];
LtoOSP(recoveredMessage.Length * 8, C);
digest.BlockUpdate(C, 0, C.Length);
if (recoveredMessage.Length != 0)
{
digest.BlockUpdate(recoveredMessage, 0, recoveredMessage.Length);
}
digest.BlockUpdate(m2Hash, 0, m2Hash.Length);
// Update for the salt
if (standardSalt != null)
{
digest.BlockUpdate(standardSalt, 0, standardSalt.Length);
}
else
{
digest.BlockUpdate(block, mStart + recoveredMessage.Length, saltLength);
}
byte[] hash = new byte[digest.GetDigestSize()];
digest.DoFinal(hash, 0);
int off = block.Length - tLength - hash.Length;
bool isOkay = true;
for (int i = 0; i != hash.Length; i++)
{
if (hash[i] != block[off + i])
{
isOkay = false;
}
}
ClearBlock(block);
ClearBlock(hash);
if (!isOkay)
{
fullMessage = false;
messageLength = 0;
ClearBlock(recoveredMessage);
return false;
}
//
// if they've input a message check what we've recovered against
// what was input.
//
if (messageLength != 0)
{
if (!IsSameAs(mBuf, recoveredMessage))
{
messageLength = 0;
ClearBlock(mBuf);
return false;
}
}
messageLength = 0;
ClearBlock(mBuf);
return true;
}
/// <summary>
/// Return true if the full message was recoveredMessage.
/// </summary>
/// <returns>true on full message recovery, false otherwise, or if not sure.</returns>
/// <seealso cref="ISignerWithRecovery.HasFullMessage"/>
public virtual bool HasFullMessage()
{
return fullMessage;
}
/// <summary> int to octet string.</summary>
/// <summary> int to octet string.</summary>
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)((uint)i >> 0);
}
/// <summary> long to octet string.</summary>
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)((ulong)l >> 0);
}
/// <summary> mask generator function, as described in Pkcs1v2.</summary>
private byte[] MaskGeneratorFunction1(
byte[] Z,
int zOff,
int zLen,
int length)
{
byte[] mask = new byte[length];
byte[] hashBuf = new byte[hLen];
byte[] C = new byte[4];
int counter = 0;
digest.Reset();
do
{
ItoOSP(counter, C);
digest.BlockUpdate(Z, zOff, zLen);
digest.BlockUpdate(C, 0, C.Length);
digest.DoFinal(hashBuf, 0);
Array.Copy(hashBuf, 0, mask, counter * hLen, hLen);
}
while (++counter < (length / hLen));
if ((counter * hLen) < length)
{
ItoOSP(counter, C);
digest.BlockUpdate(Z, zOff, zLen);
digest.BlockUpdate(C, 0, C.Length);
digest.DoFinal(hashBuf, 0);
Array.Copy(hashBuf, 0, mask, counter * hLen, mask.Length - (counter * hLen));
}
return mask;
}
}
}
#pragma warning restore
#endif