Pkcs1Encoding.cs 12 KB

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