Iso9796d2Signer.cs 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560
  1. #if !BESTHTTP_DISABLE_ALTERNATE_SSL && (!UNITY_WEBGL || UNITY_EDITOR)
  2. #pragma warning disable
  3. using System;
  4. using System.Collections;
  5. using BestHTTP.SecureProtocol.Org.BouncyCastle.Crypto;
  6. using BestHTTP.SecureProtocol.Org.BouncyCastle.Crypto.Digests;
  7. using BestHTTP.SecureProtocol.Org.BouncyCastle.Crypto.Parameters;
  8. using BestHTTP.SecureProtocol.Org.BouncyCastle.Utilities;
  9. namespace BestHTTP.SecureProtocol.Org.BouncyCastle.Crypto.Signers
  10. {
  11. /// <summary> ISO9796-2 - mechanism using a hash function with recovery (scheme 1)</summary>
  12. public class Iso9796d2Signer : ISignerWithRecovery
  13. {
  14. /// <summary>
  15. /// Return a reference to the recoveredMessage message.
  16. /// </summary>
  17. /// <returns>The full/partial recoveredMessage message.</returns>
  18. /// <seealso cref="ISignerWithRecovery.GetRecoveredMessage"/>
  19. public byte[] GetRecoveredMessage()
  20. {
  21. return recoveredMessage;
  22. }
  23. public const int TrailerImplicit = 0xBC;
  24. public const int TrailerRipeMD160 = 0x31CC;
  25. public const int TrailerRipeMD128 = 0x32CC;
  26. public const int TrailerSha1 = 0x33CC;
  27. public const int TrailerSha256 = 0x34CC;
  28. public const int TrailerSha512 = 0x35CC;
  29. public const int TrailerSha384 = 0x36CC;
  30. public const int TrailerWhirlpool = 0x37CC;
  31. private IDigest digest;
  32. private IAsymmetricBlockCipher cipher;
  33. private int trailer;
  34. private int keyBits;
  35. private byte[] block;
  36. private byte[] mBuf;
  37. private int messageLength;
  38. private bool fullMessage;
  39. private byte[] recoveredMessage;
  40. private byte[] preSig;
  41. private byte[] preBlock;
  42. /// <summary>
  43. /// Generate a signer with either implicit or explicit trailers for ISO9796-2.
  44. /// </summary>
  45. /// <param name="cipher">base cipher to use for signature creation/verification</param>
  46. /// <param name="digest">digest to use.</param>
  47. /// <param name="isImplicit">whether or not the trailer is implicit or gives the hash.</param>
  48. public Iso9796d2Signer(
  49. IAsymmetricBlockCipher cipher,
  50. IDigest digest,
  51. bool isImplicit)
  52. {
  53. this.cipher = cipher;
  54. this.digest = digest;
  55. if (isImplicit)
  56. {
  57. trailer = IsoTrailers.TRAILER_IMPLICIT;
  58. }
  59. else if (IsoTrailers.NoTrailerAvailable(digest))
  60. {
  61. throw new ArgumentException("no valid trailer", "digest");
  62. }
  63. else
  64. {
  65. trailer = IsoTrailers.GetTrailer(digest);
  66. }
  67. }
  68. /// <summary> Constructor for a signer with an explicit digest trailer.
  69. ///
  70. /// </summary>
  71. /// <param name="cipher">cipher to use.
  72. /// </param>
  73. /// <param name="digest">digest to sign with.
  74. /// </param>
  75. public Iso9796d2Signer(IAsymmetricBlockCipher cipher, IDigest digest)
  76. : this(cipher, digest, false)
  77. {
  78. }
  79. public virtual string AlgorithmName
  80. {
  81. get { return digest.AlgorithmName + "with" + "ISO9796-2S1"; }
  82. }
  83. public virtual void Init(bool forSigning, ICipherParameters parameters)
  84. {
  85. RsaKeyParameters kParam = (RsaKeyParameters) parameters;
  86. cipher.Init(forSigning, kParam);
  87. keyBits = kParam.Modulus.BitLength;
  88. block = new byte[(keyBits + 7) / 8];
  89. if (trailer == IsoTrailers.TRAILER_IMPLICIT)
  90. {
  91. mBuf = new byte[block.Length - digest.GetDigestSize() - 2];
  92. }
  93. else
  94. {
  95. mBuf = new byte[block.Length - digest.GetDigestSize() - 3];
  96. }
  97. Reset();
  98. }
  99. /// <summary> compare two byte arrays - constant time.</summary>
  100. private bool IsSameAs(byte[] a, byte[] b)
  101. {
  102. int checkLen;
  103. if (messageLength > mBuf.Length)
  104. {
  105. if (mBuf.Length > b.Length)
  106. {
  107. return false;
  108. }
  109. checkLen = mBuf.Length;
  110. }
  111. else
  112. {
  113. if (messageLength != b.Length)
  114. {
  115. return false;
  116. }
  117. checkLen = b.Length;
  118. }
  119. bool isOkay = true;
  120. for (int i = 0; i != checkLen; i++)
  121. {
  122. if (a[i] != b[i])
  123. {
  124. isOkay = false;
  125. }
  126. }
  127. return isOkay;
  128. }
  129. /// <summary> clear possible sensitive data</summary>
  130. private void ClearBlock(
  131. byte[] block)
  132. {
  133. Array.Clear(block, 0, block.Length);
  134. }
  135. public virtual void UpdateWithRecoveredMessage(
  136. byte[] signature)
  137. {
  138. byte[] block = cipher.ProcessBlock(signature, 0, signature.Length);
  139. if (((block[0] & 0xC0) ^ 0x40) != 0)
  140. throw new InvalidCipherTextException("malformed signature");
  141. if (((block[block.Length - 1] & 0xF) ^ 0xC) != 0)
  142. throw new InvalidCipherTextException("malformed signature");
  143. int delta = 0;
  144. if (((block[block.Length - 1] & 0xFF) ^ 0xBC) == 0)
  145. {
  146. delta = 1;
  147. }
  148. else
  149. {
  150. int sigTrail = ((block[block.Length - 2] & 0xFF) << 8) | (block[block.Length - 1] & 0xFF);
  151. if (IsoTrailers.NoTrailerAvailable(digest))
  152. throw new ArgumentException("unrecognised hash in signature");
  153. if (sigTrail != IsoTrailers.GetTrailer(digest))
  154. throw new InvalidOperationException("signer initialised with wrong digest for trailer " + sigTrail);
  155. delta = 2;
  156. }
  157. //
  158. // find out how much padding we've got
  159. //
  160. int mStart = 0;
  161. for (mStart = 0; mStart != block.Length; mStart++)
  162. {
  163. if (((block[mStart] & 0x0f) ^ 0x0a) == 0)
  164. break;
  165. }
  166. mStart++;
  167. int off = block.Length - delta - digest.GetDigestSize();
  168. //
  169. // there must be at least one byte of message string
  170. //
  171. if ((off - mStart) <= 0)
  172. throw new InvalidCipherTextException("malformed block");
  173. //
  174. // if we contain the whole message as well, check the hash of that.
  175. //
  176. if ((block[0] & 0x20) == 0)
  177. {
  178. fullMessage = true;
  179. recoveredMessage = new byte[off - mStart];
  180. Array.Copy(block, mStart, recoveredMessage, 0, recoveredMessage.Length);
  181. }
  182. else
  183. {
  184. fullMessage = false;
  185. recoveredMessage = new byte[off - mStart];
  186. Array.Copy(block, mStart, recoveredMessage, 0, recoveredMessage.Length);
  187. }
  188. preSig = signature;
  189. preBlock = block;
  190. digest.BlockUpdate(recoveredMessage, 0, recoveredMessage.Length);
  191. messageLength = recoveredMessage.Length;
  192. recoveredMessage.CopyTo(mBuf, 0);
  193. }
  194. /// <summary> update the internal digest with the byte b</summary>
  195. public virtual void Update(
  196. byte input)
  197. {
  198. digest.Update(input);
  199. if (messageLength < mBuf.Length)
  200. {
  201. mBuf[messageLength] = input;
  202. }
  203. messageLength++;
  204. }
  205. /// <summary> update the internal digest with the byte array in</summary>
  206. public virtual void BlockUpdate(
  207. byte[] input,
  208. int inOff,
  209. int length)
  210. {
  211. while (length > 0 && messageLength < mBuf.Length)
  212. {
  213. //for (int i = 0; i < length && (i + messageLength) < mBuf.Length; i++)
  214. //{
  215. // mBuf[messageLength + i] = input[inOff + i];
  216. //}
  217. this.Update(input[inOff]);
  218. inOff++;
  219. length--;
  220. }
  221. digest.BlockUpdate(input, inOff, length);
  222. messageLength += length;
  223. }
  224. /// <summary> reset the internal state</summary>
  225. public virtual void Reset()
  226. {
  227. digest.Reset();
  228. messageLength = 0;
  229. ClearBlock(mBuf);
  230. if (recoveredMessage != null)
  231. {
  232. ClearBlock(recoveredMessage);
  233. }
  234. recoveredMessage = null;
  235. fullMessage = false;
  236. if (preSig != null)
  237. {
  238. preSig = null;
  239. ClearBlock(preBlock);
  240. preBlock = null;
  241. }
  242. }
  243. /// <summary> Generate a signature for the loaded message using the key we were
  244. /// initialised with.
  245. /// </summary>
  246. public virtual byte[] GenerateSignature()
  247. {
  248. int digSize = digest.GetDigestSize();
  249. int t = 0;
  250. int delta = 0;
  251. if (trailer == IsoTrailers.TRAILER_IMPLICIT)
  252. {
  253. t = 8;
  254. delta = block.Length - digSize - 1;
  255. digest.DoFinal(block, delta);
  256. block[block.Length - 1] = (byte)IsoTrailers.TRAILER_IMPLICIT;
  257. }
  258. else
  259. {
  260. t = 16;
  261. delta = block.Length - digSize - 2;
  262. digest.DoFinal(block, delta);
  263. block[block.Length - 2] = (byte) ((uint)trailer >> 8);
  264. block[block.Length - 1] = (byte) trailer;
  265. }
  266. byte header = 0;
  267. int x = (digSize + messageLength) * 8 + t + 4 - keyBits;
  268. if (x > 0)
  269. {
  270. int mR = messageLength - ((x + 7) / 8);
  271. header = (byte) (0x60);
  272. delta -= mR;
  273. Array.Copy(mBuf, 0, block, delta, mR);
  274. }
  275. else
  276. {
  277. header = (byte) (0x40);
  278. delta -= messageLength;
  279. Array.Copy(mBuf, 0, block, delta, messageLength);
  280. }
  281. if ((delta - 1) > 0)
  282. {
  283. for (int i = delta - 1; i != 0; i--)
  284. {
  285. block[i] = (byte) 0xbb;
  286. }
  287. block[delta - 1] ^= (byte) 0x01;
  288. block[0] = (byte) 0x0b;
  289. block[0] |= header;
  290. }
  291. else
  292. {
  293. block[0] = (byte) 0x0a;
  294. block[0] |= header;
  295. }
  296. byte[] b = cipher.ProcessBlock(block, 0, block.Length);
  297. messageLength = 0;
  298. ClearBlock(mBuf);
  299. ClearBlock(block);
  300. return b;
  301. }
  302. /// <summary> return true if the signature represents a ISO9796-2 signature
  303. /// for the passed in message.
  304. /// </summary>
  305. public virtual bool VerifySignature(byte[] signature)
  306. {
  307. byte[] block;
  308. if (preSig == null)
  309. {
  310. try
  311. {
  312. block = cipher.ProcessBlock(signature, 0, signature.Length);
  313. }
  314. catch (Exception)
  315. {
  316. return false;
  317. }
  318. }
  319. else
  320. {
  321. if (!Arrays.AreEqual(preSig, signature))
  322. throw new InvalidOperationException("updateWithRecoveredMessage called on different signature");
  323. block = preBlock;
  324. preSig = null;
  325. preBlock = null;
  326. }
  327. if (((block[0] & 0xC0) ^ 0x40) != 0)
  328. return ReturnFalse(block);
  329. if (((block[block.Length - 1] & 0xF) ^ 0xC) != 0)
  330. return ReturnFalse(block);
  331. int delta = 0;
  332. if (((block[block.Length - 1] & 0xFF) ^ 0xBC) == 0)
  333. {
  334. delta = 1;
  335. }
  336. else
  337. {
  338. int sigTrail = ((block[block.Length - 2] & 0xFF) << 8) | (block[block.Length - 1] & 0xFF);
  339. if (IsoTrailers.NoTrailerAvailable(digest))
  340. throw new ArgumentException("unrecognised hash in signature");
  341. if (sigTrail != IsoTrailers.GetTrailer(digest))
  342. throw new InvalidOperationException("signer initialised with wrong digest for trailer " + sigTrail);
  343. delta = 2;
  344. }
  345. //
  346. // find out how much padding we've got
  347. //
  348. int mStart = 0;
  349. for (; mStart != block.Length; mStart++)
  350. {
  351. if (((block[mStart] & 0x0f) ^ 0x0a) == 0)
  352. {
  353. break;
  354. }
  355. }
  356. mStart++;
  357. //
  358. // check the hashes
  359. //
  360. byte[] hash = new byte[digest.GetDigestSize()];
  361. int off = block.Length - delta - hash.Length;
  362. //
  363. // there must be at least one byte of message string
  364. //
  365. if ((off - mStart) <= 0)
  366. {
  367. return ReturnFalse(block);
  368. }
  369. //
  370. // if we contain the whole message as well, check the hash of that.
  371. //
  372. if ((block[0] & 0x20) == 0)
  373. {
  374. fullMessage = true;
  375. // check right number of bytes passed in.
  376. if (messageLength > off - mStart)
  377. {
  378. return ReturnFalse(block);
  379. }
  380. digest.Reset();
  381. digest.BlockUpdate(block, mStart, off - mStart);
  382. digest.DoFinal(hash, 0);
  383. bool isOkay = true;
  384. for (int i = 0; i != hash.Length; i++)
  385. {
  386. block[off + i] ^= hash[i];
  387. if (block[off + i] != 0)
  388. {
  389. isOkay = false;
  390. }
  391. }
  392. if (!isOkay)
  393. {
  394. return ReturnFalse(block);
  395. }
  396. recoveredMessage = new byte[off - mStart];
  397. Array.Copy(block, mStart, recoveredMessage, 0, recoveredMessage.Length);
  398. }
  399. else
  400. {
  401. fullMessage = false;
  402. digest.DoFinal(hash, 0);
  403. bool isOkay = true;
  404. for (int i = 0; i != hash.Length; i++)
  405. {
  406. block[off + i] ^= hash[i];
  407. if (block[off + i] != 0)
  408. {
  409. isOkay = false;
  410. }
  411. }
  412. if (!isOkay)
  413. {
  414. return ReturnFalse(block);
  415. }
  416. recoveredMessage = new byte[off - mStart];
  417. Array.Copy(block, mStart, recoveredMessage, 0, recoveredMessage.Length);
  418. }
  419. //
  420. // if they've input a message check what we've recovered against
  421. // what was input.
  422. //
  423. if (messageLength != 0)
  424. {
  425. if (!IsSameAs(mBuf, recoveredMessage))
  426. {
  427. return ReturnFalse(block);
  428. }
  429. }
  430. ClearBlock(mBuf);
  431. ClearBlock(block);
  432. messageLength = 0;
  433. return true;
  434. }
  435. private bool ReturnFalse(byte[] block)
  436. {
  437. messageLength = 0;
  438. ClearBlock(mBuf);
  439. ClearBlock(block);
  440. return false;
  441. }
  442. /// <summary>
  443. /// Return true if the full message was recoveredMessage.
  444. /// </summary>
  445. /// <returns> true on full message recovery, false otherwise.</returns>
  446. /// <seealso cref="ISignerWithRecovery.HasFullMessage"/>
  447. public virtual bool HasFullMessage()
  448. {
  449. return fullMessage;
  450. }
  451. }
  452. }
  453. #pragma warning restore
  454. #endif