Iso9796d2PssSigner.cs 18 KB

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