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