RC2WrapEngine.cs 9.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374
  1. #if !BESTHTTP_DISABLE_ALTERNATE_SSL && (!UNITY_WEBGL || UNITY_EDITOR)
  2. using System;
  3. using Org.BouncyCastle.Crypto.Digests;
  4. using Org.BouncyCastle.Crypto.Modes;
  5. using Org.BouncyCastle.Crypto.Parameters;
  6. using Org.BouncyCastle.Security;
  7. using Org.BouncyCastle.Utilities;
  8. namespace Org.BouncyCastle.Crypto.Engines
  9. {
  10. /**
  11. * Wrap keys according to RFC 3217 - RC2 mechanism
  12. */
  13. public class RC2WrapEngine
  14. : IWrapper
  15. {
  16. /** Field engine */
  17. private CbcBlockCipher engine;
  18. /** Field param */
  19. private ICipherParameters parameters;
  20. /** Field paramPlusIV */
  21. private ParametersWithIV paramPlusIV;
  22. /** Field iv */
  23. private byte[] iv;
  24. /** Field forWrapping */
  25. private bool forWrapping;
  26. private SecureRandom sr;
  27. /** Field IV2 */
  28. private static readonly byte[] IV2 =
  29. {
  30. (byte) 0x4a, (byte) 0xdd, (byte) 0xa2,
  31. (byte) 0x2c, (byte) 0x79, (byte) 0xe8,
  32. (byte) 0x21, (byte) 0x05
  33. };
  34. //
  35. // checksum digest
  36. //
  37. IDigest sha1 = new Sha1Digest();
  38. byte[] digest = new byte[20];
  39. /**
  40. * Method init
  41. *
  42. * @param forWrapping
  43. * @param param
  44. */
  45. public virtual void Init(
  46. bool forWrapping,
  47. ICipherParameters parameters)
  48. {
  49. this.forWrapping = forWrapping;
  50. this.engine = new CbcBlockCipher(new RC2Engine());
  51. if (parameters is ParametersWithRandom)
  52. {
  53. ParametersWithRandom pWithR = (ParametersWithRandom)parameters;
  54. sr = pWithR.Random;
  55. parameters = pWithR.Parameters;
  56. }
  57. else
  58. {
  59. sr = new SecureRandom();
  60. }
  61. if (parameters is ParametersWithIV)
  62. {
  63. if (!forWrapping)
  64. throw new ArgumentException("You should not supply an IV for unwrapping");
  65. this.paramPlusIV = (ParametersWithIV)parameters;
  66. this.iv = this.paramPlusIV.GetIV();
  67. this.parameters = this.paramPlusIV.Parameters;
  68. if (this.iv.Length != 8)
  69. throw new ArgumentException("IV is not 8 octets");
  70. }
  71. else
  72. {
  73. this.parameters = parameters;
  74. if (this.forWrapping)
  75. {
  76. // Hm, we have no IV but we want to wrap ?!?
  77. // well, then we have to create our own IV.
  78. this.iv = new byte[8];
  79. sr.NextBytes(iv);
  80. this.paramPlusIV = new ParametersWithIV(this.parameters, this.iv);
  81. }
  82. }
  83. }
  84. /**
  85. * Method GetAlgorithmName
  86. *
  87. * @return
  88. */
  89. public virtual string AlgorithmName
  90. {
  91. get { return "RC2"; }
  92. }
  93. /**
  94. * Method wrap
  95. *
  96. * @param in
  97. * @param inOff
  98. * @param inLen
  99. * @return
  100. */
  101. public virtual byte[] Wrap(
  102. byte[] input,
  103. int inOff,
  104. int length)
  105. {
  106. if (!forWrapping)
  107. {
  108. throw new InvalidOperationException("Not initialized for wrapping");
  109. }
  110. int len = length + 1;
  111. if ((len % 8) != 0)
  112. {
  113. len += 8 - (len % 8);
  114. }
  115. byte [] keyToBeWrapped = new byte[len];
  116. keyToBeWrapped[0] = (byte)length;
  117. Array.Copy(input, inOff, keyToBeWrapped, 1, length);
  118. byte[] pad = new byte[keyToBeWrapped.Length - length - 1];
  119. if (pad.Length > 0)
  120. {
  121. sr.NextBytes(pad);
  122. Array.Copy(pad, 0, keyToBeWrapped, length + 1, pad.Length);
  123. }
  124. // Compute the CMS Key Checksum, (section 5.6.1), call this CKS.
  125. byte[] CKS = CalculateCmsKeyChecksum(keyToBeWrapped);
  126. // Let WKCKS = WK || CKS where || is concatenation.
  127. byte[] WKCKS = new byte[keyToBeWrapped.Length + CKS.Length];
  128. Array.Copy(keyToBeWrapped, 0, WKCKS, 0, keyToBeWrapped.Length);
  129. Array.Copy(CKS, 0, WKCKS, keyToBeWrapped.Length, CKS.Length);
  130. // Encrypt WKCKS in CBC mode using KEK as the key and IV as the
  131. // initialization vector. Call the results TEMP1.
  132. byte [] TEMP1 = new byte[WKCKS.Length];
  133. Array.Copy(WKCKS, 0, TEMP1, 0, WKCKS.Length);
  134. int noOfBlocks = WKCKS.Length / engine.GetBlockSize();
  135. int extraBytes = WKCKS.Length % engine.GetBlockSize();
  136. if (extraBytes != 0)
  137. {
  138. throw new InvalidOperationException("Not multiple of block length");
  139. }
  140. engine.Init(true, paramPlusIV);
  141. for (int i = 0; i < noOfBlocks; i++)
  142. {
  143. int currentBytePos = i * engine.GetBlockSize();
  144. engine.ProcessBlock(TEMP1, currentBytePos, TEMP1, currentBytePos);
  145. }
  146. // Left TEMP2 = IV || TEMP1.
  147. byte[] TEMP2 = new byte[this.iv.Length + TEMP1.Length];
  148. Array.Copy(this.iv, 0, TEMP2, 0, this.iv.Length);
  149. Array.Copy(TEMP1, 0, TEMP2, this.iv.Length, TEMP1.Length);
  150. // Reverse the order of the octets in TEMP2 and call the result TEMP3.
  151. byte[] TEMP3 = new byte[TEMP2.Length];
  152. for (int i = 0; i < TEMP2.Length; i++)
  153. {
  154. TEMP3[i] = TEMP2[TEMP2.Length - (i + 1)];
  155. }
  156. // Encrypt TEMP3 in CBC mode using the KEK and an initialization vector
  157. // of 0x 4a dd a2 2c 79 e8 21 05. The resulting cipher text is the desired
  158. // result. It is 40 octets long if a 168 bit key is being wrapped.
  159. ParametersWithIV param2 = new ParametersWithIV(this.parameters, IV2);
  160. this.engine.Init(true, param2);
  161. for (int i = 0; i < noOfBlocks + 1; i++)
  162. {
  163. int currentBytePos = i * engine.GetBlockSize();
  164. engine.ProcessBlock(TEMP3, currentBytePos, TEMP3, currentBytePos);
  165. }
  166. return TEMP3;
  167. }
  168. /**
  169. * Method unwrap
  170. *
  171. * @param in
  172. * @param inOff
  173. * @param inLen
  174. * @return
  175. * @throws InvalidCipherTextException
  176. */
  177. public virtual byte[] Unwrap(
  178. byte[] input,
  179. int inOff,
  180. int length)
  181. {
  182. if (forWrapping)
  183. {
  184. throw new InvalidOperationException("Not set for unwrapping");
  185. }
  186. if (input == null)
  187. {
  188. throw new InvalidCipherTextException("Null pointer as ciphertext");
  189. }
  190. if (length % engine.GetBlockSize() != 0)
  191. {
  192. throw new InvalidCipherTextException("Ciphertext not multiple of "
  193. + engine.GetBlockSize());
  194. }
  195. /*
  196. // Check if the length of the cipher text is reasonable given the key
  197. // type. It must be 40 bytes for a 168 bit key and either 32, 40, or
  198. // 48 bytes for a 128, 192, or 256 bit key. If the length is not supported
  199. // or inconsistent with the algorithm for which the key is intended,
  200. // return error.
  201. //
  202. // we do not accept 168 bit keys. it has to be 192 bit.
  203. int lengthA = (estimatedKeyLengthInBit / 8) + 16;
  204. int lengthB = estimatedKeyLengthInBit % 8;
  205. if ((lengthA != keyToBeUnwrapped.Length) || (lengthB != 0)) {
  206. throw new XMLSecurityException("empty");
  207. }
  208. */
  209. // Decrypt the cipher text with TRIPLedeS in CBC mode using the KEK
  210. // and an initialization vector (IV) of 0x4adda22c79e82105. Call the output TEMP3.
  211. ParametersWithIV param2 = new ParametersWithIV(this.parameters, IV2);
  212. this.engine.Init(false, param2);
  213. byte [] TEMP3 = new byte[length];
  214. Array.Copy(input, inOff, TEMP3, 0, length);
  215. for (int i = 0; i < (TEMP3.Length / engine.GetBlockSize()); i++)
  216. {
  217. int currentBytePos = i * engine.GetBlockSize();
  218. engine.ProcessBlock(TEMP3, currentBytePos, TEMP3, currentBytePos);
  219. }
  220. // Reverse the order of the octets in TEMP3 and call the result TEMP2.
  221. byte[] TEMP2 = new byte[TEMP3.Length];
  222. for (int i = 0; i < TEMP3.Length; i++)
  223. {
  224. TEMP2[i] = TEMP3[TEMP3.Length - (i + 1)];
  225. }
  226. // Decompose TEMP2 into IV, the first 8 octets, and TEMP1, the remaining octets.
  227. this.iv = new byte[8];
  228. byte[] TEMP1 = new byte[TEMP2.Length - 8];
  229. Array.Copy(TEMP2, 0, this.iv, 0, 8);
  230. Array.Copy(TEMP2, 8, TEMP1, 0, TEMP2.Length - 8);
  231. // Decrypt TEMP1 using TRIPLedeS in CBC mode using the KEK and the IV
  232. // found in the previous step. Call the result WKCKS.
  233. this.paramPlusIV = new ParametersWithIV(this.parameters, this.iv);
  234. this.engine.Init(false, this.paramPlusIV);
  235. byte[] LCEKPADICV = new byte[TEMP1.Length];
  236. Array.Copy(TEMP1, 0, LCEKPADICV, 0, TEMP1.Length);
  237. for (int i = 0; i < (LCEKPADICV.Length / engine.GetBlockSize()); i++)
  238. {
  239. int currentBytePos = i * engine.GetBlockSize();
  240. engine.ProcessBlock(LCEKPADICV, currentBytePos, LCEKPADICV, currentBytePos);
  241. }
  242. // Decompose LCEKPADICV. CKS is the last 8 octets and WK, the wrapped key, are
  243. // those octets before the CKS.
  244. byte[] result = new byte[LCEKPADICV.Length - 8];
  245. byte[] CKStoBeVerified = new byte[8];
  246. Array.Copy(LCEKPADICV, 0, result, 0, LCEKPADICV.Length - 8);
  247. Array.Copy(LCEKPADICV, LCEKPADICV.Length - 8, CKStoBeVerified, 0, 8);
  248. // Calculate a CMS Key Checksum, (section 5.6.1), over the WK and compare
  249. // with the CKS extracted in the above step. If they are not equal, return error.
  250. if (!CheckCmsKeyChecksum(result, CKStoBeVerified))
  251. {
  252. throw new InvalidCipherTextException(
  253. "Checksum inside ciphertext is corrupted");
  254. }
  255. if ((result.Length - ((result[0] & 0xff) + 1)) > 7)
  256. {
  257. throw new InvalidCipherTextException(
  258. "too many pad bytes (" + (result.Length - ((result[0] & 0xff) + 1)) + ")");
  259. }
  260. // CEK is the wrapped key, now extracted for use in data decryption.
  261. byte[] CEK = new byte[result[0]];
  262. Array.Copy(result, 1, CEK, 0, CEK.Length);
  263. return CEK;
  264. }
  265. /**
  266. * Some key wrap algorithms make use of the Key Checksum defined
  267. * in CMS [CMS-Algorithms]. This is used to provide an integrity
  268. * check value for the key being wrapped. The algorithm is
  269. *
  270. * - Compute the 20 octet SHA-1 hash on the key being wrapped.
  271. * - Use the first 8 octets of this hash as the checksum value.
  272. *
  273. * @param key
  274. * @return
  275. * @throws Exception
  276. * @see http://www.w3.org/TR/xmlenc-core/#sec-CMSKeyChecksum
  277. */
  278. private byte[] CalculateCmsKeyChecksum(
  279. byte[] key)
  280. {
  281. sha1.BlockUpdate(key, 0, key.Length);
  282. sha1.DoFinal(digest, 0);
  283. byte[] result = new byte[8];
  284. Array.Copy(digest, 0, result, 0, 8);
  285. return result;
  286. }
  287. /**
  288. * @param key
  289. * @param checksum
  290. * @return
  291. * @see http://www.w3.org/TR/xmlenc-core/#sec-CMSKeyChecksum
  292. */
  293. private bool CheckCmsKeyChecksum(
  294. byte[] key,
  295. byte[] checksum)
  296. {
  297. return Arrays.ConstantTimeAreEqual(CalculateCmsKeyChecksum(key), checksum);
  298. }
  299. }
  300. }
  301. #endif