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