#if !BESTHTTP_DISABLE_ALTERNATE_SSL && (!UNITY_WEBGL || UNITY_EDITOR) #pragma warning disable using System; using System.Collections; using BestHTTP.SecureProtocol.Org.BouncyCastle.Crypto; using BestHTTP.SecureProtocol.Org.BouncyCastle.Crypto.Digests; using BestHTTP.SecureProtocol.Org.BouncyCastle.Crypto.Parameters; using BestHTTP.SecureProtocol.Org.BouncyCastle.Utilities; namespace BestHTTP.SecureProtocol.Org.BouncyCastle.Crypto.Signers { /// ISO9796-2 - mechanism using a hash function with recovery (scheme 1) public class Iso9796d2Signer : ISignerWithRecovery { /// /// Return a reference to the recoveredMessage message. /// /// The full/partial recoveredMessage message. /// 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 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; /// /// Generate a signer with either implicit or explicit trailers for ISO9796-2. /// /// base cipher to use for signature creation/verification /// digest to use. /// whether or not the trailer is implicit or gives the hash. public Iso9796d2Signer( IAsymmetricBlockCipher cipher, IDigest digest, bool isImplicit) { this.cipher = cipher; this.digest = digest; if (isImplicit) { trailer = IsoTrailers.TRAILER_IMPLICIT; } else if (IsoTrailers.NoTrailerAvailable(digest)) { throw new ArgumentException("no valid trailer", "digest"); } else { trailer = IsoTrailers.GetTrailer(digest); } } /// Constructor for a signer with an explicit digest trailer. /// /// /// cipher to use. /// /// digest to sign with. /// public Iso9796d2Signer(IAsymmetricBlockCipher cipher, IDigest digest) : this(cipher, digest, false) { } public virtual string AlgorithmName { get { return digest.AlgorithmName + "with" + "ISO9796-2S1"; } } public virtual void Init(bool forSigning, ICipherParameters parameters) { RsaKeyParameters kParam = (RsaKeyParameters) parameters; 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() - 2]; } else { mBuf = new byte[block.Length - digest.GetDigestSize() - 3]; } Reset(); } /// compare two byte arrays - constant time. private bool IsSameAs(byte[] a, byte[] b) { int checkLen; if (messageLength > mBuf.Length) { if (mBuf.Length > b.Length) { return false; } checkLen = mBuf.Length; } else { if (messageLength != b.Length) { return false; } checkLen = b.Length; } bool isOkay = true; for (int i = 0; i != checkLen; i++) { if (a[i] != b[i]) { isOkay = false; } } return isOkay; } /// clear possible sensitive data 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); if (((block[0] & 0xC0) ^ 0x40) != 0) throw new InvalidCipherTextException("malformed signature"); if (((block[block.Length - 1] & 0xF) ^ 0xC) != 0) throw new InvalidCipherTextException("malformed signature"); int delta = 0; if (((block[block.Length - 1] & 0xFF) ^ 0xBC) == 0) { delta = 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); delta = 2; } // // find out how much padding we've got // int mStart = 0; for (mStart = 0; mStart != block.Length; mStart++) { if (((block[mStart] & 0x0f) ^ 0x0a) == 0) break; } mStart++; int off = block.Length - delta - digest.GetDigestSize(); // // there must be at least one byte of message string // if ((off - mStart) <= 0) throw new InvalidCipherTextException("malformed block"); // // if we contain the whole message as well, check the hash of that. // if ((block[0] & 0x20) == 0) { fullMessage = true; recoveredMessage = new byte[off - mStart]; Array.Copy(block, mStart, recoveredMessage, 0, recoveredMessage.Length); } else { fullMessage = false; recoveredMessage = new byte[off - mStart]; Array.Copy(block, mStart, recoveredMessage, 0, recoveredMessage.Length); } preSig = signature; preBlock = block; digest.BlockUpdate(recoveredMessage, 0, recoveredMessage.Length); messageLength = recoveredMessage.Length; recoveredMessage.CopyTo(mBuf, 0); } /// update the internal digest with the byte b public virtual void Update( byte input) { digest.Update(input); if (messageLength < mBuf.Length) { mBuf[messageLength] = input; } messageLength++; } /// update the internal digest with the byte array in public virtual void BlockUpdate( byte[] input, int inOff, int length) { while (length > 0 && messageLength < mBuf.Length) { //for (int i = 0; i < length && (i + messageLength) < mBuf.Length; i++) //{ // mBuf[messageLength + i] = input[inOff + i]; //} this.Update(input[inOff]); inOff++; length--; } digest.BlockUpdate(input, inOff, length); messageLength += length; } /// reset the internal state 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; } } /// Generate a signature for the loaded message using the key we were /// initialised with. /// public virtual byte[] GenerateSignature() { int digSize = digest.GetDigestSize(); int t = 0; int delta = 0; if (trailer == IsoTrailers.TRAILER_IMPLICIT) { t = 8; delta = block.Length - digSize - 1; digest.DoFinal(block, delta); block[block.Length - 1] = (byte)IsoTrailers.TRAILER_IMPLICIT; } else { t = 16; delta = block.Length - digSize - 2; digest.DoFinal(block, delta); block[block.Length - 2] = (byte) ((uint)trailer >> 8); block[block.Length - 1] = (byte) trailer; } byte header = 0; int x = (digSize + messageLength) * 8 + t + 4 - keyBits; if (x > 0) { int mR = messageLength - ((x + 7) / 8); header = (byte) (0x60); delta -= mR; Array.Copy(mBuf, 0, block, delta, mR); } else { header = (byte) (0x40); delta -= messageLength; Array.Copy(mBuf, 0, block, delta, messageLength); } if ((delta - 1) > 0) { for (int i = delta - 1; i != 0; i--) { block[i] = (byte) 0xbb; } block[delta - 1] ^= (byte) 0x01; block[0] = (byte) 0x0b; block[0] |= header; } else { block[0] = (byte) 0x0a; block[0] |= header; } byte[] b = cipher.ProcessBlock(block, 0, block.Length); messageLength = 0; ClearBlock(mBuf); ClearBlock(block); return b; } /// return true if the signature represents a ISO9796-2 signature /// for the passed in message. /// public virtual bool VerifySignature(byte[] signature) { byte[] block; if (preSig == null) { try { block = cipher.ProcessBlock(signature, 0, signature.Length); } catch (Exception) { return false; } } else { if (!Arrays.AreEqual(preSig, signature)) throw new InvalidOperationException("updateWithRecoveredMessage called on different signature"); block = preBlock; preSig = null; preBlock = null; } if (((block[0] & 0xC0) ^ 0x40) != 0) return ReturnFalse(block); if (((block[block.Length - 1] & 0xF) ^ 0xC) != 0) return ReturnFalse(block); int delta = 0; if (((block[block.Length - 1] & 0xFF) ^ 0xBC) == 0) { delta = 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); delta = 2; } // // find out how much padding we've got // int mStart = 0; for (; mStart != block.Length; mStart++) { if (((block[mStart] & 0x0f) ^ 0x0a) == 0) { break; } } mStart++; // // check the hashes // byte[] hash = new byte[digest.GetDigestSize()]; int off = block.Length - delta - hash.Length; // // there must be at least one byte of message string // if ((off - mStart) <= 0) { return ReturnFalse(block); } // // if we contain the whole message as well, check the hash of that. // if ((block[0] & 0x20) == 0) { fullMessage = true; // check right number of bytes passed in. if (messageLength > off - mStart) { return ReturnFalse(block); } digest.Reset(); digest.BlockUpdate(block, mStart, off - mStart); digest.DoFinal(hash, 0); bool isOkay = true; for (int i = 0; i != hash.Length; i++) { block[off + i] ^= hash[i]; if (block[off + i] != 0) { isOkay = false; } } if (!isOkay) { return ReturnFalse(block); } recoveredMessage = new byte[off - mStart]; Array.Copy(block, mStart, recoveredMessage, 0, recoveredMessage.Length); } else { fullMessage = false; digest.DoFinal(hash, 0); bool isOkay = true; for (int i = 0; i != hash.Length; i++) { block[off + i] ^= hash[i]; if (block[off + i] != 0) { isOkay = false; } } if (!isOkay) { return ReturnFalse(block); } recoveredMessage = new byte[off - mStart]; Array.Copy(block, mStart, recoveredMessage, 0, recoveredMessage.Length); } // // if they've input a message check what we've recovered against // what was input. // if (messageLength != 0) { if (!IsSameAs(mBuf, recoveredMessage)) { return ReturnFalse(block); } } ClearBlock(mBuf); ClearBlock(block); messageLength = 0; return true; } private bool ReturnFalse(byte[] block) { messageLength = 0; ClearBlock(mBuf); ClearBlock(block); return false; } /// /// Return true if the full message was recoveredMessage. /// /// true on full message recovery, false otherwise. /// public virtual bool HasFullMessage() { return fullMessage; } } } #pragma warning restore #endif