Pkcs1Encoding.cs 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380
  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.Crypto.Digests;
  6. using Best.HTTP.SecureProtocol.Org.BouncyCastle.Security;
  7. using Best.HTTP.SecureProtocol.Org.BouncyCastle.Utilities;
  8. namespace Best.HTTP.SecureProtocol.Org.BouncyCastle.Crypto.Encodings
  9. {
  10. /**
  11. * this does your basic Pkcs 1 v1.5 padding - whether or not you should be using this
  12. * depends on your application - see Pkcs1 Version 2 for details.
  13. */
  14. public class Pkcs1Encoding
  15. : IAsymmetricBlockCipher
  16. {
  17. /**
  18. * some providers fail to include the leading zero in PKCS1 encoded blocks. If you need to
  19. * work with one of these set the system property Best.HTTP.SecureProtocol.Org.BouncyCastle.Pkcs1.Strict to false.
  20. */
  21. public const string StrictLengthEnabledProperty = "Best.HTTP.SecureProtocol.Org.BouncyCastle.Pkcs1.Strict";
  22. private const int HeaderLength = 10;
  23. /**
  24. * The same effect can be achieved by setting the static property directly
  25. * <p>
  26. * The static property is checked during construction of the encoding object, it is set to
  27. * true by default.
  28. * </p>
  29. */
  30. public static bool StrictLengthEnabled
  31. {
  32. get { return strictLengthEnabled[0]; }
  33. set { strictLengthEnabled[0] = value; }
  34. }
  35. private static readonly bool[] strictLengthEnabled;
  36. static Pkcs1Encoding()
  37. {
  38. string strictProperty = Org.BouncyCastle.Utilities.Platform.GetEnvironmentVariable(StrictLengthEnabledProperty);
  39. strictLengthEnabled = new bool[]{ strictProperty == null || Org.BouncyCastle.Utilities.Platform.EqualsIgnoreCase("true", strictProperty) };
  40. }
  41. private SecureRandom random;
  42. private IAsymmetricBlockCipher engine;
  43. private bool forEncryption;
  44. private bool forPrivateKey;
  45. private bool useStrictLength;
  46. private int pLen = -1;
  47. private byte[] fallback = null;
  48. private byte[] blockBuffer = null;
  49. /**
  50. * Basic constructor.
  51. *
  52. * @param cipher
  53. */
  54. public Pkcs1Encoding(
  55. IAsymmetricBlockCipher cipher)
  56. {
  57. this.engine = cipher;
  58. this.useStrictLength = StrictLengthEnabled;
  59. }
  60. /**
  61. * Constructor for decryption with a fixed plaintext length.
  62. *
  63. * @param cipher The cipher to use for cryptographic operation.
  64. * @param pLen Length of the expected plaintext.
  65. */
  66. public Pkcs1Encoding(IAsymmetricBlockCipher cipher, int pLen)
  67. {
  68. this.engine = cipher;
  69. this.useStrictLength = StrictLengthEnabled;
  70. this.pLen = pLen;
  71. }
  72. /**
  73. * Constructor for decryption with a fixed plaintext length and a fallback
  74. * value that is returned, if the padding is incorrect.
  75. *
  76. * @param cipher
  77. * The cipher to use for cryptographic operation.
  78. * @param fallback
  79. * The fallback value, we don't to a arraycopy here.
  80. */
  81. public Pkcs1Encoding(IAsymmetricBlockCipher cipher, byte[] fallback)
  82. {
  83. this.engine = cipher;
  84. this.useStrictLength = StrictLengthEnabled;
  85. this.fallback = fallback;
  86. this.pLen = fallback.Length;
  87. }
  88. public string AlgorithmName => engine.AlgorithmName + "/PKCS1Padding";
  89. public IAsymmetricBlockCipher UnderlyingCipher => engine;
  90. public void Init(bool forEncryption, ICipherParameters parameters)
  91. {
  92. AsymmetricKeyParameter kParam;
  93. if (parameters is ParametersWithRandom withRandom)
  94. {
  95. this.random = withRandom.Random;
  96. kParam = (AsymmetricKeyParameter)withRandom.Parameters;
  97. }
  98. else
  99. {
  100. this.random = CryptoServicesRegistrar.GetSecureRandom();
  101. kParam = (AsymmetricKeyParameter)parameters;
  102. }
  103. engine.Init(forEncryption, parameters);
  104. this.forPrivateKey = kParam.IsPrivate;
  105. this.forEncryption = forEncryption;
  106. this.blockBuffer = new byte[engine.GetOutputBlockSize()];
  107. if (pLen > 0 && fallback == null && random == null)
  108. throw new ArgumentException("encoder requires random");
  109. }
  110. public int GetInputBlockSize()
  111. {
  112. int baseBlockSize = engine.GetInputBlockSize();
  113. return forEncryption
  114. ? baseBlockSize - HeaderLength
  115. : baseBlockSize;
  116. }
  117. public int GetOutputBlockSize()
  118. {
  119. int baseBlockSize = engine.GetOutputBlockSize();
  120. return forEncryption
  121. ? baseBlockSize
  122. : baseBlockSize - HeaderLength;
  123. }
  124. public byte[] ProcessBlock(
  125. byte[] input,
  126. int inOff,
  127. int length)
  128. {
  129. return forEncryption
  130. ? EncodeBlock(input, inOff, length)
  131. : DecodeBlock(input, inOff, length);
  132. }
  133. private byte[] EncodeBlock(
  134. byte[] input,
  135. int inOff,
  136. int inLen)
  137. {
  138. if (inLen > GetInputBlockSize())
  139. throw new ArgumentException("input data too large", "inLen");
  140. byte[] block = new byte[engine.GetInputBlockSize()];
  141. if (forPrivateKey)
  142. {
  143. block[0] = 0x01; // type code 1
  144. for (int i = 1; i != block.Length - inLen - 1; i++)
  145. {
  146. block[i] = (byte)0xFF;
  147. }
  148. }
  149. else
  150. {
  151. random.NextBytes(block); // random fill
  152. block[0] = 0x02; // type code 2
  153. //
  154. // a zero byte marks the end of the padding, so all
  155. // the pad bytes must be non-zero.
  156. //
  157. for (int i = 1; i != block.Length - inLen - 1; i++)
  158. {
  159. while (block[i] == 0)
  160. {
  161. block[i] = (byte)random.NextInt();
  162. }
  163. }
  164. }
  165. block[block.Length - inLen - 1] = 0x00; // mark the end of the padding
  166. Array.Copy(input, inOff, block, block.Length - inLen, inLen);
  167. return engine.ProcessBlock(block, 0, block.Length);
  168. }
  169. /**
  170. * Checks if the argument is a correctly PKCS#1.5 encoded Plaintext
  171. * for encryption.
  172. *
  173. * @param encoded The Plaintext.
  174. * @param pLen Expected length of the plaintext.
  175. * @return Either 0, if the encoding is correct, or -1, if it is incorrect.
  176. */
  177. private static int CheckPkcs1Encoding(byte[] encoded, int pLen)
  178. {
  179. int correct = 0;
  180. /*
  181. * Check if the first two bytes are 0 2
  182. */
  183. correct |= (encoded[0] ^ 2);
  184. /*
  185. * Now the padding check, check for no 0 byte in the padding
  186. */
  187. int plen = encoded.Length - (
  188. pLen /* Length of the PMS */
  189. + 1 /* Final 0-byte before PMS */
  190. );
  191. for (int i = 1; i < plen; i++)
  192. {
  193. int tmp = encoded[i];
  194. tmp |= tmp >> 1;
  195. tmp |= tmp >> 2;
  196. tmp |= tmp >> 4;
  197. correct |= (tmp & 1) - 1;
  198. }
  199. /*
  200. * Make sure the padding ends with a 0 byte.
  201. */
  202. correct |= encoded[encoded.Length - (pLen + 1)];
  203. /*
  204. * Return 0 or 1, depending on the result.
  205. */
  206. correct |= correct >> 1;
  207. correct |= correct >> 2;
  208. correct |= correct >> 4;
  209. return ~((correct & 1) - 1);
  210. }
  211. /**
  212. * Decode PKCS#1.5 encoding, and return a random value if the padding is not correct.
  213. *
  214. * @param in The encrypted block.
  215. * @param inOff Offset in the encrypted block.
  216. * @param inLen Length of the encrypted block.
  217. * @param pLen Length of the desired output.
  218. * @return The plaintext without padding, or a random value if the padding was incorrect.
  219. * @throws InvalidCipherTextException
  220. */
  221. private byte[] DecodeBlockOrRandom(byte[] input, int inOff, int inLen)
  222. {
  223. if (!forPrivateKey)
  224. throw new InvalidCipherTextException("sorry, this method is only for decryption, not for signing");
  225. byte[] block = engine.ProcessBlock(input, inOff, inLen);
  226. byte[] random;
  227. if (this.fallback == null)
  228. {
  229. random = new byte[this.pLen];
  230. this.random.NextBytes(random);
  231. }
  232. else
  233. {
  234. random = fallback;
  235. }
  236. byte[] data = (useStrictLength & (block.Length != engine.GetOutputBlockSize())) ? blockBuffer : block;
  237. /*
  238. * Check the padding.
  239. */
  240. int correct = CheckPkcs1Encoding(data, this.pLen);
  241. /*
  242. * Now, to a constant time constant memory copy of the decrypted value
  243. * or the random value, depending on the validity of the padding.
  244. */
  245. byte[] result = new byte[this.pLen];
  246. for (int i = 0; i < this.pLen; i++)
  247. {
  248. result[i] = (byte)((data[i + (data.Length - pLen)] & (~correct)) | (random[i] & correct));
  249. }
  250. Arrays.Fill(data, 0);
  251. return result;
  252. }
  253. /**
  254. * @exception InvalidCipherTextException if the decrypted block is not in Pkcs1 format.
  255. */
  256. private byte[] DecodeBlock(
  257. byte[] input,
  258. int inOff,
  259. int inLen)
  260. {
  261. /*
  262. * If the length of the expected plaintext is known, we use a constant-time decryption.
  263. * If the decryption fails, we return a random value.
  264. */
  265. if (this.pLen != -1)
  266. {
  267. return this.DecodeBlockOrRandom(input, inOff, inLen);
  268. }
  269. byte[] block = engine.ProcessBlock(input, inOff, inLen);
  270. bool incorrectLength = (useStrictLength & (block.Length != engine.GetOutputBlockSize()));
  271. byte[] data;
  272. if (block.Length < GetOutputBlockSize())
  273. {
  274. data = blockBuffer;
  275. }
  276. else
  277. {
  278. data = block;
  279. }
  280. byte expectedType = (byte)(forPrivateKey ? 2 : 1);
  281. byte type = data[0];
  282. bool badType = (type != expectedType);
  283. //
  284. // find and extract the message block.
  285. //
  286. int start = FindStart(type, data);
  287. start++; // data should start at the next byte
  288. if (badType | (start < HeaderLength))
  289. {
  290. Arrays.Fill(data, 0);
  291. throw new InvalidCipherTextException("block incorrect");
  292. }
  293. // if we get this far, it's likely to be a genuine encoding error
  294. if (incorrectLength)
  295. {
  296. Arrays.Fill(data, 0);
  297. throw new InvalidCipherTextException("block incorrect size");
  298. }
  299. byte[] result = new byte[data.Length - start];
  300. Array.Copy(data, start, result, 0, result.Length);
  301. return result;
  302. }
  303. private int FindStart(byte type, byte[] block)
  304. {
  305. int start = -1;
  306. bool padErr = false;
  307. for (int i = 1; i != block.Length; i++)
  308. {
  309. byte pad = block[i];
  310. if (pad == 0 & start < 0)
  311. {
  312. start = i;
  313. }
  314. padErr |= ((type == 1) & (start < 0) & (pad != (byte)0xff));
  315. }
  316. return padErr ? -1 : start;
  317. }
  318. }
  319. }
  320. #pragma warning restore
  321. #endif