Pkcs1Encoding.cs 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388
  1. #if !BESTHTTP_DISABLE_ALTERNATE_SSL && (!UNITY_WEBGL || UNITY_EDITOR)
  2. #pragma warning disable
  3. using System;
  4. using BestHTTP.SecureProtocol.Org.BouncyCastle.Crypto.Parameters;
  5. using BestHTTP.SecureProtocol.Org.BouncyCastle.Crypto.Digests;
  6. using BestHTTP.SecureProtocol.Org.BouncyCastle.Security;
  7. using BestHTTP.SecureProtocol.Org.BouncyCastle.Utilities;
  8. namespace BestHTTP.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 BestHTTP.SecureProtocol.Org.BouncyCastle.Pkcs1.Strict to false.
  20. */
  21. public const string StrictLengthEnabledProperty = "BestHTTP.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 = BestHTTP.SecureProtocol.Org.BouncyCastle.Utilities.Platform.GetEnvironmentVariable(StrictLengthEnabledProperty);
  39. strictLengthEnabled = new bool[]{ strictProperty == null || BestHTTP.SecureProtocol.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 IAsymmetricBlockCipher GetUnderlyingCipher()
  89. {
  90. return engine;
  91. }
  92. public string AlgorithmName
  93. {
  94. get { return engine.AlgorithmName + "/PKCS1Padding"; }
  95. }
  96. public void Init(bool forEncryption, ICipherParameters parameters)
  97. {
  98. AsymmetricKeyParameter kParam;
  99. if (parameters is ParametersWithRandom)
  100. {
  101. ParametersWithRandom rParam = (ParametersWithRandom)parameters;
  102. this.random = rParam.Random;
  103. kParam = (AsymmetricKeyParameter)rParam.Parameters;
  104. }
  105. else
  106. {
  107. this.random = new SecureRandom();
  108. kParam = (AsymmetricKeyParameter)parameters;
  109. }
  110. engine.Init(forEncryption, parameters);
  111. this.forPrivateKey = kParam.IsPrivate;
  112. this.forEncryption = forEncryption;
  113. this.blockBuffer = new byte[engine.GetOutputBlockSize()];
  114. if (pLen > 0 && fallback == null && random == null)
  115. throw new ArgumentException("encoder requires random");
  116. }
  117. public int GetInputBlockSize()
  118. {
  119. int baseBlockSize = engine.GetInputBlockSize();
  120. return forEncryption
  121. ? baseBlockSize - HeaderLength
  122. : baseBlockSize;
  123. }
  124. public int GetOutputBlockSize()
  125. {
  126. int baseBlockSize = engine.GetOutputBlockSize();
  127. return forEncryption
  128. ? baseBlockSize
  129. : baseBlockSize - HeaderLength;
  130. }
  131. public byte[] ProcessBlock(
  132. byte[] input,
  133. int inOff,
  134. int length)
  135. {
  136. return forEncryption
  137. ? EncodeBlock(input, inOff, length)
  138. : DecodeBlock(input, inOff, length);
  139. }
  140. private byte[] EncodeBlock(
  141. byte[] input,
  142. int inOff,
  143. int inLen)
  144. {
  145. if (inLen > GetInputBlockSize())
  146. throw new ArgumentException("input data too large", "inLen");
  147. byte[] block = new byte[engine.GetInputBlockSize()];
  148. if (forPrivateKey)
  149. {
  150. block[0] = 0x01; // type code 1
  151. for (int i = 1; i != block.Length - inLen - 1; i++)
  152. {
  153. block[i] = (byte)0xFF;
  154. }
  155. }
  156. else
  157. {
  158. random.NextBytes(block); // random fill
  159. block[0] = 0x02; // type code 2
  160. //
  161. // a zero byte marks the end of the padding, so all
  162. // the pad bytes must be non-zero.
  163. //
  164. for (int i = 1; i != block.Length - inLen - 1; i++)
  165. {
  166. while (block[i] == 0)
  167. {
  168. block[i] = (byte)random.NextInt();
  169. }
  170. }
  171. }
  172. block[block.Length - inLen - 1] = 0x00; // mark the end of the padding
  173. Array.Copy(input, inOff, block, block.Length - inLen, inLen);
  174. return engine.ProcessBlock(block, 0, block.Length);
  175. }
  176. /**
  177. * Checks if the argument is a correctly PKCS#1.5 encoded Plaintext
  178. * for encryption.
  179. *
  180. * @param encoded The Plaintext.
  181. * @param pLen Expected length of the plaintext.
  182. * @return Either 0, if the encoding is correct, or -1, if it is incorrect.
  183. */
  184. private static int CheckPkcs1Encoding(byte[] encoded, int pLen)
  185. {
  186. int correct = 0;
  187. /*
  188. * Check if the first two bytes are 0 2
  189. */
  190. correct |= (encoded[0] ^ 2);
  191. /*
  192. * Now the padding check, check for no 0 byte in the padding
  193. */
  194. int plen = encoded.Length - (
  195. pLen /* Length of the PMS */
  196. + 1 /* Final 0-byte before PMS */
  197. );
  198. for (int i = 1; i < plen; i++)
  199. {
  200. int tmp = encoded[i];
  201. tmp |= tmp >> 1;
  202. tmp |= tmp >> 2;
  203. tmp |= tmp >> 4;
  204. correct |= (tmp & 1) - 1;
  205. }
  206. /*
  207. * Make sure the padding ends with a 0 byte.
  208. */
  209. correct |= encoded[encoded.Length - (pLen + 1)];
  210. /*
  211. * Return 0 or 1, depending on the result.
  212. */
  213. correct |= correct >> 1;
  214. correct |= correct >> 2;
  215. correct |= correct >> 4;
  216. return ~((correct & 1) - 1);
  217. }
  218. /**
  219. * Decode PKCS#1.5 encoding, and return a random value if the padding is not correct.
  220. *
  221. * @param in The encrypted block.
  222. * @param inOff Offset in the encrypted block.
  223. * @param inLen Length of the encrypted block.
  224. * @param pLen Length of the desired output.
  225. * @return The plaintext without padding, or a random value if the padding was incorrect.
  226. * @throws InvalidCipherTextException
  227. */
  228. private byte[] DecodeBlockOrRandom(byte[] input, int inOff, int inLen)
  229. {
  230. if (!forPrivateKey)
  231. throw new InvalidCipherTextException("sorry, this method is only for decryption, not for signing");
  232. byte[] block = engine.ProcessBlock(input, inOff, inLen);
  233. byte[] random;
  234. if (this.fallback == null)
  235. {
  236. random = new byte[this.pLen];
  237. this.random.NextBytes(random);
  238. }
  239. else
  240. {
  241. random = fallback;
  242. }
  243. byte[] data = (useStrictLength & (block.Length != engine.GetOutputBlockSize())) ? blockBuffer : block;
  244. /*
  245. * Check the padding.
  246. */
  247. int correct = CheckPkcs1Encoding(data, this.pLen);
  248. /*
  249. * Now, to a constant time constant memory copy of the decrypted value
  250. * or the random value, depending on the validity of the padding.
  251. */
  252. byte[] result = new byte[this.pLen];
  253. for (int i = 0; i < this.pLen; i++)
  254. {
  255. result[i] = (byte)((data[i + (data.Length - pLen)] & (~correct)) | (random[i] & correct));
  256. }
  257. Arrays.Fill(data, 0);
  258. return result;
  259. }
  260. /**
  261. * @exception InvalidCipherTextException if the decrypted block is not in Pkcs1 format.
  262. */
  263. private byte[] DecodeBlock(
  264. byte[] input,
  265. int inOff,
  266. int inLen)
  267. {
  268. /*
  269. * If the length of the expected plaintext is known, we use a constant-time decryption.
  270. * If the decryption fails, we return a random value.
  271. */
  272. if (this.pLen != -1)
  273. {
  274. return this.DecodeBlockOrRandom(input, inOff, inLen);
  275. }
  276. byte[] block = engine.ProcessBlock(input, inOff, inLen);
  277. bool incorrectLength = (useStrictLength & (block.Length != engine.GetOutputBlockSize()));
  278. byte[] data;
  279. if (block.Length < GetOutputBlockSize())
  280. {
  281. data = blockBuffer;
  282. }
  283. else
  284. {
  285. data = block;
  286. }
  287. byte expectedType = (byte)(forPrivateKey ? 2 : 1);
  288. byte type = data[0];
  289. bool badType = (type != expectedType);
  290. //
  291. // find and extract the message block.
  292. //
  293. int start = FindStart(type, data);
  294. start++; // data should start at the next byte
  295. if (badType | (start < HeaderLength))
  296. {
  297. Arrays.Fill(data, 0);
  298. throw new InvalidCipherTextException("block incorrect");
  299. }
  300. // if we get this far, it's likely to be a genuine encoding error
  301. if (incorrectLength)
  302. {
  303. Arrays.Fill(data, 0);
  304. throw new InvalidCipherTextException("block incorrect size");
  305. }
  306. byte[] result = new byte[data.Length - start];
  307. Array.Copy(data, start, result, 0, result.Length);
  308. return result;
  309. }
  310. private int FindStart(byte type, byte[] block)
  311. {
  312. int start = -1;
  313. bool padErr = false;
  314. for (int i = 1; i != block.Length; i++)
  315. {
  316. byte pad = block[i];
  317. if (pad == 0 & start < 0)
  318. {
  319. start = i;
  320. }
  321. padErr |= ((type == 1) & (start < 0) & (pad != (byte)0xff));
  322. }
  323. return padErr ? -1 : start;
  324. }
  325. }
  326. }
  327. #pragma warning restore
  328. #endif