Iso9796d2PssSigner.cs 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623
  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.Digests;
  6. using BestHTTP.SecureProtocol.Org.BouncyCastle.Crypto.Parameters;
  7. using BestHTTP.SecureProtocol.Org.BouncyCastle.Security;
  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 2 and 3).
  12. /// <p>
  13. /// Note: the usual length for the salt is the length of the hash
  14. /// function used in bytes.</p>
  15. /// </summary>
  16. public class Iso9796d2PssSigner
  17. : ISignerWithRecovery
  18. {
  19. /// <summary>
  20. /// Return a reference to the recoveredMessage message.
  21. /// </summary>
  22. /// <returns>The full/partial recoveredMessage message.</returns>
  23. /// <seealso cref="ISignerWithRecovery.GetRecoveredMessage"/>
  24. public byte[] GetRecoveredMessage()
  25. {
  26. return recoveredMessage;
  27. }
  28. [Obsolete("Use 'IsoTrailers' instead")]
  29. public const int TrailerImplicit = 0xBC;
  30. [Obsolete("Use 'IsoTrailers' instead")]
  31. public const int TrailerRipeMD160 = 0x31CC;
  32. [Obsolete("Use 'IsoTrailers' instead")]
  33. public const int TrailerRipeMD128 = 0x32CC;
  34. [Obsolete("Use 'IsoTrailers' instead")]
  35. public const int TrailerSha1 = 0x33CC;
  36. [Obsolete("Use 'IsoTrailers' instead")]
  37. public const int TrailerSha256 = 0x34CC;
  38. [Obsolete("Use 'IsoTrailers' instead")]
  39. public const int TrailerSha512 = 0x35CC;
  40. [Obsolete("Use 'IsoTrailers' instead")]
  41. public const int TrailerSha384 = 0x36CC;
  42. [Obsolete("Use 'IsoTrailers' instead")]
  43. public const int TrailerWhirlpool = 0x37CC;
  44. private IDigest digest;
  45. private IAsymmetricBlockCipher cipher;
  46. private SecureRandom random;
  47. private byte[] standardSalt;
  48. private int hLen;
  49. private int trailer;
  50. private int keyBits;
  51. private byte[] block;
  52. private byte[] mBuf;
  53. private int messageLength;
  54. private readonly int saltLength;
  55. private bool fullMessage;
  56. private byte[] recoveredMessage;
  57. private byte[] preSig;
  58. private byte[] preBlock;
  59. private int preMStart;
  60. private int preTLength;
  61. /// <summary>
  62. /// Generate a signer with either implicit or explicit trailers for ISO9796-2, scheme 2 or 3.
  63. /// </summary>
  64. /// <param name="cipher">base cipher to use for signature creation/verification</param>
  65. /// <param name="digest">digest to use.</param>
  66. /// <param name="saltLength">length of salt in bytes.</param>
  67. /// <param name="isImplicit">whether or not the trailer is implicit or gives the hash.</param>
  68. public Iso9796d2PssSigner(
  69. IAsymmetricBlockCipher cipher,
  70. IDigest digest,
  71. int saltLength,
  72. bool isImplicit)
  73. {
  74. this.cipher = cipher;
  75. this.digest = digest;
  76. this.hLen = digest.GetDigestSize();
  77. this.saltLength = saltLength;
  78. if (isImplicit)
  79. {
  80. trailer = IsoTrailers.TRAILER_IMPLICIT;
  81. }
  82. else if (IsoTrailers.NoTrailerAvailable(digest))
  83. {
  84. throw new ArgumentException("no valid trailer", "digest");
  85. }
  86. else
  87. {
  88. trailer = IsoTrailers.GetTrailer(digest);
  89. }
  90. }
  91. /// <summary> Constructor for a signer with an explicit digest trailer.
  92. ///
  93. /// </summary>
  94. /// <param name="cipher">cipher to use.
  95. /// </param>
  96. /// <param name="digest">digest to sign with.
  97. /// </param>
  98. /// <param name="saltLength">length of salt in bytes.
  99. /// </param>
  100. public Iso9796d2PssSigner(
  101. IAsymmetricBlockCipher cipher,
  102. IDigest digest,
  103. int saltLength)
  104. : this(cipher, digest, saltLength, false)
  105. {
  106. }
  107. public virtual string AlgorithmName
  108. {
  109. get { return digest.AlgorithmName + "with" + "ISO9796-2S2"; }
  110. }
  111. /// <summary>Initialise the signer.</summary>
  112. /// <param name="forSigning">true if for signing, false if for verification.</param>
  113. /// <param name="parameters">parameters for signature generation/verification. If the
  114. /// parameters are for generation they should be a ParametersWithRandom,
  115. /// a ParametersWithSalt, or just an RsaKeyParameters object. If RsaKeyParameters
  116. /// are passed in a SecureRandom will be created.
  117. /// </param>
  118. /// <exception cref="ArgumentException">if wrong parameter type or a fixed
  119. /// salt is passed in which is the wrong length.
  120. /// </exception>
  121. public virtual void Init(
  122. bool forSigning,
  123. ICipherParameters parameters)
  124. {
  125. RsaKeyParameters kParam;
  126. if (parameters is ParametersWithRandom)
  127. {
  128. ParametersWithRandom p = (ParametersWithRandom) parameters;
  129. kParam = (RsaKeyParameters) p.Parameters;
  130. if (forSigning)
  131. {
  132. random = p.Random;
  133. }
  134. }
  135. else if (parameters is ParametersWithSalt)
  136. {
  137. if (!forSigning)
  138. throw new ArgumentException("ParametersWithSalt only valid for signing", "parameters");
  139. ParametersWithSalt p = (ParametersWithSalt) parameters;
  140. kParam = (RsaKeyParameters) p.Parameters;
  141. standardSalt = p.GetSalt();
  142. if (standardSalt.Length != saltLength)
  143. throw new ArgumentException("Fixed salt is of wrong length");
  144. }
  145. else
  146. {
  147. kParam = (RsaKeyParameters) parameters;
  148. if (forSigning)
  149. {
  150. random = new SecureRandom();
  151. }
  152. }
  153. cipher.Init(forSigning, kParam);
  154. keyBits = kParam.Modulus.BitLength;
  155. block = new byte[(keyBits + 7) / 8];
  156. if (trailer == IsoTrailers.TRAILER_IMPLICIT)
  157. {
  158. mBuf = new byte[block.Length - digest.GetDigestSize() - saltLength - 1 - 1];
  159. }
  160. else
  161. {
  162. mBuf = new byte[block.Length - digest.GetDigestSize() - saltLength - 1 - 2];
  163. }
  164. Reset();
  165. }
  166. /// <summary> compare two byte arrays - constant time.</summary>
  167. private bool IsSameAs(byte[] a, byte[] b)
  168. {
  169. if (messageLength != b.Length)
  170. {
  171. return false;
  172. }
  173. bool isOkay = true;
  174. for (int i = 0; i != b.Length; i++)
  175. {
  176. if (a[i] != b[i])
  177. {
  178. isOkay = false;
  179. }
  180. }
  181. return isOkay;
  182. }
  183. /// <summary> clear possible sensitive data</summary>
  184. private void ClearBlock(
  185. byte[] block)
  186. {
  187. Array.Clear(block, 0, block.Length);
  188. }
  189. public virtual void UpdateWithRecoveredMessage(
  190. byte[] signature)
  191. {
  192. byte[] block = cipher.ProcessBlock(signature, 0, signature.Length);
  193. //
  194. // adjust block size for leading zeroes if necessary
  195. //
  196. if (block.Length < (keyBits + 7) / 8)
  197. {
  198. byte[] tmp = new byte[(keyBits + 7) / 8];
  199. Array.Copy(block, 0, tmp, tmp.Length - block.Length, block.Length);
  200. ClearBlock(block);
  201. block = tmp;
  202. }
  203. int tLength;
  204. if (((block[block.Length - 1] & 0xFF) ^ 0xBC) == 0)
  205. {
  206. tLength = 1;
  207. }
  208. else
  209. {
  210. int sigTrail = ((block[block.Length - 2] & 0xFF) << 8) | (block[block.Length - 1] & 0xFF);
  211. if (IsoTrailers.NoTrailerAvailable(digest))
  212. throw new ArgumentException("unrecognised hash in signature");
  213. if (sigTrail != IsoTrailers.GetTrailer(digest))
  214. throw new InvalidOperationException("signer initialised with wrong digest for trailer " + sigTrail);
  215. tLength = 2;
  216. }
  217. //
  218. // calculate H(m2)
  219. //
  220. byte[] m2Hash = new byte[hLen];
  221. digest.DoFinal(m2Hash, 0);
  222. //
  223. // remove the mask
  224. //
  225. byte[] dbMask = MaskGeneratorFunction1(block, block.Length - hLen - tLength, hLen, block.Length - hLen - tLength);
  226. for (int i = 0; i != dbMask.Length; i++)
  227. {
  228. block[i] ^= dbMask[i];
  229. }
  230. block[0] &= 0x7f;
  231. //
  232. // find out how much padding we've got
  233. //
  234. int mStart = 0;
  235. while (mStart < block.Length)
  236. {
  237. if (block[mStart++] == 0x01)
  238. break;
  239. }
  240. if (mStart >= block.Length)
  241. {
  242. ClearBlock(block);
  243. }
  244. fullMessage = (mStart > 1);
  245. recoveredMessage = new byte[dbMask.Length - mStart - saltLength];
  246. Array.Copy(block, mStart, recoveredMessage, 0, recoveredMessage.Length);
  247. recoveredMessage.CopyTo(mBuf, 0);
  248. preSig = signature;
  249. preBlock = block;
  250. preMStart = mStart;
  251. preTLength = tLength;
  252. }
  253. /// <summary> update the internal digest with the byte b</summary>
  254. public virtual void Update(
  255. byte input)
  256. {
  257. if (preSig == null && messageLength < mBuf.Length)
  258. {
  259. mBuf[messageLength++] = input;
  260. }
  261. else
  262. {
  263. digest.Update(input);
  264. }
  265. }
  266. /// <summary> update the internal digest with the byte array in</summary>
  267. public virtual void BlockUpdate(
  268. byte[] input,
  269. int inOff,
  270. int length)
  271. {
  272. if (preSig == null)
  273. {
  274. while (length > 0 && messageLength < mBuf.Length)
  275. {
  276. this.Update(input[inOff]);
  277. inOff++;
  278. length--;
  279. }
  280. }
  281. if (length > 0)
  282. {
  283. digest.BlockUpdate(input, inOff, length);
  284. }
  285. }
  286. /// <summary> reset the internal state</summary>
  287. public virtual void Reset()
  288. {
  289. digest.Reset();
  290. messageLength = 0;
  291. if (mBuf != null)
  292. {
  293. ClearBlock(mBuf);
  294. }
  295. if (recoveredMessage != null)
  296. {
  297. ClearBlock(recoveredMessage);
  298. recoveredMessage = null;
  299. }
  300. fullMessage = false;
  301. if (preSig != null)
  302. {
  303. preSig = null;
  304. ClearBlock(preBlock);
  305. preBlock = null;
  306. }
  307. }
  308. /// <summary> Generate a signature for the loaded message using the key we were
  309. /// initialised with.
  310. /// </summary>
  311. public virtual byte[] GenerateSignature()
  312. {
  313. int digSize = digest.GetDigestSize();
  314. byte[] m2Hash = new byte[digSize];
  315. digest.DoFinal(m2Hash, 0);
  316. byte[] C = new byte[8];
  317. LtoOSP(messageLength * 8, C);
  318. digest.BlockUpdate(C, 0, C.Length);
  319. digest.BlockUpdate(mBuf, 0, messageLength);
  320. digest.BlockUpdate(m2Hash, 0, m2Hash.Length);
  321. byte[] salt;
  322. if (standardSalt != null)
  323. {
  324. salt = standardSalt;
  325. }
  326. else
  327. {
  328. salt = new byte[saltLength];
  329. random.NextBytes(salt);
  330. }
  331. digest.BlockUpdate(salt, 0, salt.Length);
  332. byte[] hash = new byte[digest.GetDigestSize()];
  333. digest.DoFinal(hash, 0);
  334. int tLength = 2;
  335. if (trailer == IsoTrailers.TRAILER_IMPLICIT)
  336. {
  337. tLength = 1;
  338. }
  339. int off = block.Length - messageLength - salt.Length - hLen - tLength - 1;
  340. block[off] = (byte) (0x01);
  341. Array.Copy(mBuf, 0, block, off + 1, messageLength);
  342. Array.Copy(salt, 0, block, off + 1 + messageLength, salt.Length);
  343. byte[] dbMask = MaskGeneratorFunction1(hash, 0, hash.Length, block.Length - hLen - tLength);
  344. for (int i = 0; i != dbMask.Length; i++)
  345. {
  346. block[i] ^= dbMask[i];
  347. }
  348. Array.Copy(hash, 0, block, block.Length - hLen - tLength, hLen);
  349. if (trailer == IsoTrailers.TRAILER_IMPLICIT)
  350. {
  351. block[block.Length - 1] = (byte)IsoTrailers.TRAILER_IMPLICIT;
  352. }
  353. else
  354. {
  355. block[block.Length - 2] = (byte) ((uint)trailer >> 8);
  356. block[block.Length - 1] = (byte) trailer;
  357. }
  358. block[0] &= (byte) (0x7f);
  359. byte[] b = cipher.ProcessBlock(block, 0, block.Length);
  360. ClearBlock(mBuf);
  361. ClearBlock(block);
  362. messageLength = 0;
  363. return b;
  364. }
  365. /// <summary> return true if the signature represents a ISO9796-2 signature
  366. /// for the passed in message.
  367. /// </summary>
  368. public virtual bool VerifySignature(
  369. byte[] signature)
  370. {
  371. //
  372. // calculate H(m2)
  373. //
  374. byte[] m2Hash = new byte[hLen];
  375. digest.DoFinal(m2Hash, 0);
  376. byte[] block;
  377. int tLength;
  378. int mStart = 0;
  379. if (preSig == null)
  380. {
  381. try
  382. {
  383. UpdateWithRecoveredMessage(signature);
  384. }
  385. catch (Exception)
  386. {
  387. return false;
  388. }
  389. }
  390. else
  391. {
  392. if (!Arrays.AreEqual(preSig, signature))
  393. {
  394. throw new InvalidOperationException("UpdateWithRecoveredMessage called on different signature");
  395. }
  396. }
  397. block = preBlock;
  398. mStart = preMStart;
  399. tLength = preTLength;
  400. preSig = null;
  401. preBlock = null;
  402. //
  403. // check the hashes
  404. //
  405. byte[] C = new byte[8];
  406. LtoOSP(recoveredMessage.Length * 8, C);
  407. digest.BlockUpdate(C, 0, C.Length);
  408. if (recoveredMessage.Length != 0)
  409. {
  410. digest.BlockUpdate(recoveredMessage, 0, recoveredMessage.Length);
  411. }
  412. digest.BlockUpdate(m2Hash, 0, m2Hash.Length);
  413. // Update for the salt
  414. if (standardSalt != null)
  415. {
  416. digest.BlockUpdate(standardSalt, 0, standardSalt.Length);
  417. }
  418. else
  419. {
  420. digest.BlockUpdate(block, mStart + recoveredMessage.Length, saltLength);
  421. }
  422. byte[] hash = new byte[digest.GetDigestSize()];
  423. digest.DoFinal(hash, 0);
  424. int off = block.Length - tLength - hash.Length;
  425. bool isOkay = true;
  426. for (int i = 0; i != hash.Length; i++)
  427. {
  428. if (hash[i] != block[off + i])
  429. {
  430. isOkay = false;
  431. }
  432. }
  433. ClearBlock(block);
  434. ClearBlock(hash);
  435. if (!isOkay)
  436. {
  437. fullMessage = false;
  438. messageLength = 0;
  439. ClearBlock(recoveredMessage);
  440. return false;
  441. }
  442. //
  443. // if they've input a message check what we've recovered against
  444. // what was input.
  445. //
  446. if (messageLength != 0)
  447. {
  448. if (!IsSameAs(mBuf, recoveredMessage))
  449. {
  450. messageLength = 0;
  451. ClearBlock(mBuf);
  452. return false;
  453. }
  454. }
  455. messageLength = 0;
  456. ClearBlock(mBuf);
  457. return true;
  458. }
  459. /// <summary>
  460. /// Return true if the full message was recoveredMessage.
  461. /// </summary>
  462. /// <returns>true on full message recovery, false otherwise, or if not sure.</returns>
  463. /// <seealso cref="ISignerWithRecovery.HasFullMessage"/>
  464. public virtual bool HasFullMessage()
  465. {
  466. return fullMessage;
  467. }
  468. /// <summary> int to octet string.</summary>
  469. /// <summary> int to octet string.</summary>
  470. private void ItoOSP(
  471. int i,
  472. byte[] sp)
  473. {
  474. sp[0] = (byte)((uint)i >> 24);
  475. sp[1] = (byte)((uint)i >> 16);
  476. sp[2] = (byte)((uint)i >> 8);
  477. sp[3] = (byte)((uint)i >> 0);
  478. }
  479. /// <summary> long to octet string.</summary>
  480. private void LtoOSP(long l, byte[] sp)
  481. {
  482. sp[0] = (byte)((ulong)l >> 56);
  483. sp[1] = (byte)((ulong)l >> 48);
  484. sp[2] = (byte)((ulong)l >> 40);
  485. sp[3] = (byte)((ulong)l >> 32);
  486. sp[4] = (byte)((ulong)l >> 24);
  487. sp[5] = (byte)((ulong)l >> 16);
  488. sp[6] = (byte)((ulong)l >> 8);
  489. sp[7] = (byte)((ulong)l >> 0);
  490. }
  491. /// <summary> mask generator function, as described in Pkcs1v2.</summary>
  492. private byte[] MaskGeneratorFunction1(
  493. byte[] Z,
  494. int zOff,
  495. int zLen,
  496. int length)
  497. {
  498. byte[] mask = new byte[length];
  499. byte[] hashBuf = new byte[hLen];
  500. byte[] C = new byte[4];
  501. int counter = 0;
  502. digest.Reset();
  503. do
  504. {
  505. ItoOSP(counter, C);
  506. digest.BlockUpdate(Z, zOff, zLen);
  507. digest.BlockUpdate(C, 0, C.Length);
  508. digest.DoFinal(hashBuf, 0);
  509. Array.Copy(hashBuf, 0, mask, counter * hLen, hLen);
  510. }
  511. while (++counter < (length / hLen));
  512. if ((counter * hLen) < length)
  513. {
  514. ItoOSP(counter, C);
  515. digest.BlockUpdate(Z, zOff, zLen);
  516. digest.BlockUpdate(C, 0, C.Length);
  517. digest.DoFinal(hashBuf, 0);
  518. Array.Copy(hashBuf, 0, mask, counter * hLen, mask.Length - (counter * hLen));
  519. }
  520. return mask;
  521. }
  522. }
  523. }
  524. #pragma warning restore
  525. #endif