SymmetricAlgorithm.cs 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491
  1. //
  2. // System.Security.Cryptography SymmetricAlgorithm Class implementation
  3. //
  4. // Authors:
  5. // Thomas Neidhart ([email protected])
  6. // Sebastien Pouliot ([email protected])
  7. //
  8. // Portions (C) 2002 Motus Technologies Inc. (http://www.motus.com)
  9. //
  10. using System;
  11. namespace System.Security.Cryptography {
  12. // This class implement most of the common code required for symmetric
  13. // algorithm transforms, like:
  14. // - CipherMode: Build CBC... on top of (descendant supplied) ECB
  15. // - PaddingMode, transform properties, multiple blocks, reuse...
  16. //
  17. // Descendants MUST:
  18. // - intialize themselves (like key expansion, ...)
  19. // - override the ECB (Electronic Code Book) method which will only be
  20. // called using BlockSize byte[] array.
  21. internal abstract class SymmetricTransform : ICryptoTransform {
  22. protected SymmetricAlgorithm algo;
  23. protected bool encrypt;
  24. private int BlockSizeByte;
  25. private byte[] temp;
  26. private byte[] temp2;
  27. private int FeedBackByte;
  28. private bool m_disposed = false;
  29. public SymmetricTransform (SymmetricAlgorithm symmAlgo, bool encryption, byte[] rgbIV)
  30. {
  31. algo = symmAlgo;
  32. encrypt = encryption;
  33. BlockSizeByte = (algo.BlockSize >> 3);
  34. temp = new byte [BlockSizeByte];
  35. Array.Copy (rgbIV, 0, temp, 0, BlockSizeByte);
  36. temp2 = new byte [BlockSizeByte];
  37. FeedBackByte = (algo.FeedbackSize >> 3);
  38. }
  39. ~SymmetricTransform ()
  40. {
  41. Dispose (false);
  42. }
  43. public void Dispose ()
  44. {
  45. Dispose (true);
  46. GC.SuppressFinalize (this); // Finalization is now unnecessary
  47. }
  48. // MUST be overriden by classes using unmanaged ressources
  49. // the override method must call the base class
  50. protected void Dispose (bool disposing)
  51. {
  52. if (!m_disposed) {
  53. if (disposing) {
  54. // dispose managed object: zeroize and free
  55. Array.Clear (temp, 0, BlockSizeByte);
  56. temp = null;
  57. Array.Clear (temp2, 0, BlockSizeByte);
  58. temp2 = null;
  59. }
  60. m_disposed = true;
  61. }
  62. }
  63. public virtual bool CanTransformMultipleBlocks {
  64. get { return false; }
  65. }
  66. public bool CanReuseTransform {
  67. get { return false; }
  68. }
  69. public virtual int InputBlockSize {
  70. get { return BlockSizeByte; }
  71. }
  72. public virtual int OutputBlockSize {
  73. get { return BlockSizeByte; }
  74. }
  75. // note: Each block MUST be BlockSizeValue in size!!!
  76. // i.e. Any padding must be done before calling this method
  77. protected void Transform (byte[] input, byte[] output)
  78. {
  79. switch (algo.Mode) {
  80. case CipherMode.ECB:
  81. ECB (input, output);
  82. break;
  83. case CipherMode.CBC:
  84. CBC (input, output);
  85. break;
  86. case CipherMode.CFB:
  87. CFB (input, output);
  88. break;
  89. case CipherMode.OFB:
  90. OFB (input, output);
  91. break;
  92. case CipherMode.CTS:
  93. CTS (input, output);
  94. break;
  95. default:
  96. throw new NotImplementedException ("Unkown CipherMode" + algo.Mode.ToString ());
  97. }
  98. }
  99. // Electronic Code Book (ECB)
  100. protected abstract void ECB (byte[] input, byte[] output);
  101. // Cipher-Block-Chaining (CBC)
  102. protected virtual void CBC (byte[] input, byte[] output)
  103. {
  104. if (encrypt) {
  105. for (int i = 0; i < BlockSizeByte; i++)
  106. temp[i] ^= input[i];
  107. ECB (temp, output);
  108. Array.Copy (output, 0, temp, 0, BlockSizeByte);
  109. }
  110. else {
  111. Array.Copy (input, 0, temp2, 0, BlockSizeByte);
  112. ECB (input, output);
  113. for (int i = 0; i < BlockSizeByte; i++)
  114. output[i] ^= temp[i];
  115. Array.Copy (temp2, 0, temp, 0, BlockSizeByte);
  116. }
  117. }
  118. // Cipher-FeedBack (CFB)
  119. protected virtual void CFB (byte[] input, byte[] output)
  120. {
  121. ECB (temp, temp2);
  122. if (encrypt) {
  123. for (int i = 0; i < BlockSizeByte; i++)
  124. output[i] = (byte)(temp2[i] ^ input[i]);
  125. Array.Copy (temp, FeedBackByte, temp, 0, BlockSizeByte - FeedBackByte);
  126. Array.Copy (output, 0, temp, BlockSizeByte - FeedBackByte, FeedBackByte);
  127. }
  128. else {
  129. Array.Copy (input, 0, temp, 0, BlockSizeByte);
  130. for (int i = 0; i < BlockSizeByte; i++)
  131. output[i] = (byte)(temp2[i] ^ input[i]);
  132. }
  133. }
  134. // Output-FeedBack (OFB)
  135. protected virtual void OFB (byte[] input, byte[] output)
  136. {
  137. throw new NotImplementedException ("OFB not yet supported");
  138. }
  139. // Cipher Text Stealing (CTS)
  140. protected virtual void CTS (byte[] input, byte[] output)
  141. {
  142. throw new NotImplementedException ("CTS not yet supported");
  143. }
  144. public virtual int TransformBlock (byte [] inputBuffer, int inputOffset, int inputCount, byte [] outputBuffer, int outputOffset)
  145. {
  146. if (m_disposed)
  147. throw new ObjectDisposedException ("Object is disposed");
  148. // if ((inputCount & (BlockSizeByte-1)) != 0) didn't work for Rijndael block = 192
  149. if ((inputCount % BlockSizeByte) != 0)
  150. throw new CryptographicException ("Invalid input block size.");
  151. if (outputOffset + inputCount > outputBuffer.Length)
  152. throw new CryptographicException ("Insufficient output buffer size.");
  153. int step = InputBlockSize;
  154. int offs = inputOffset;
  155. int full = inputCount / step;
  156. byte [] workBuff = new byte [step];
  157. byte[] workout = new byte [step];
  158. for (int i = 0; i < full; i++) {
  159. Array.Copy (inputBuffer, offs, workBuff, 0, step);
  160. Transform (workBuff, workout);
  161. Array.Copy (workout, 0, outputBuffer, outputOffset, step);
  162. offs += step;
  163. outputOffset += step;
  164. }
  165. return (full * step);
  166. }
  167. public virtual byte [] TransformFinalBlock (byte [] inputBuffer, int inputOffset, int inputCount)
  168. {
  169. if (m_disposed)
  170. throw new ObjectDisposedException ("Object is disposed");
  171. int num = (inputCount + BlockSizeByte) & (~(BlockSizeByte-1));
  172. byte [] res = new byte [num];
  173. //int full = (num - BlockSizeByte); // didn't work for bs 192
  174. int full = (inputCount / BlockSizeByte) * BlockSizeByte;
  175. // are there still full block to process ?
  176. if (full > 0)
  177. TransformBlock (inputBuffer, inputOffset, full, res, 0);
  178. //int rem = inputCount & (BlockSizeByte-1);
  179. int rem = inputCount - full;
  180. // is there a partial block to pad ?
  181. if (rem > 0) {
  182. if (encrypt) {
  183. int padding = BlockSizeByte - (inputCount % BlockSizeByte);
  184. switch (algo.Padding) {
  185. case PaddingMode.None:
  186. break;
  187. case PaddingMode.PKCS7:
  188. for (int i = BlockSizeByte; --i >= (BlockSizeByte - padding);)
  189. res [i] = (byte) padding;
  190. break;
  191. case PaddingMode.Zeros:
  192. for (int i = BlockSizeByte; --i >= (BlockSizeByte - padding);)
  193. res [i] = 0;
  194. break;
  195. }
  196. Array.Copy (inputBuffer, inputOffset + full, res, full, rem);
  197. // the last padded block will be transformed in-place
  198. TransformBlock (res, full, BlockSizeByte, res, full);
  199. }
  200. else {
  201. switch (algo.Padding) {
  202. case PaddingMode.None:
  203. break;
  204. case PaddingMode.PKCS7:
  205. byte padding = res [inputCount - 1];
  206. for (int i = 0; i < padding; i++) {
  207. if (res [inputCount - 1 - i] == padding)
  208. res[inputCount - 1 - i] = 0x00;
  209. }
  210. break;
  211. case PaddingMode.Zeros:
  212. break;
  213. }
  214. }
  215. }
  216. return res;
  217. }
  218. }
  219. /// <summary>
  220. /// Abstract base class for all cryptographic symmetric algorithms.
  221. /// Available algorithms include:
  222. /// DES, RC2, Rijndael, TripleDES
  223. /// </summary>
  224. public abstract class SymmetricAlgorithm {
  225. protected int BlockSizeValue; // The block size of the cryptographic operation in bits.
  226. protected int FeedbackSizeValue; // The feedback size of the cryptographic operation in bits.
  227. protected byte[] IVValue; // The initialization vector ( IV) for the symmetric algorithm.
  228. protected int KeySizeValue; // The size of the secret key used by the symmetric algorithm in bits.
  229. protected byte[] KeyValue; // The secret key for the symmetric algorithm.
  230. protected KeySizes[] LegalBlockSizesValue; // Specifies the block sizes that are supported by the symmetric algorithm.
  231. protected KeySizes[] LegalKeySizesValue; // Specifies the key sizes that are supported by the symmetric algorithm.
  232. protected CipherMode ModeValue; // Represents the cipher mode used in the symmetric algorithm.
  233. protected PaddingMode PaddingValue; // Represents the padding mode used in the symmetric algorithm.
  234. /// <summary>
  235. /// Called from constructor of derived class.
  236. /// </summary>
  237. public SymmetricAlgorithm ()
  238. {
  239. ModeValue = CipherMode.CBC;
  240. PaddingValue = PaddingMode.PKCS7;
  241. }
  242. /// <summary>
  243. /// Called from constructor of derived class.
  244. /// </summary>
  245. ~SymmetricAlgorithm ()
  246. {
  247. if (KeyValue != null) {
  248. // Zeroize the secret key and free
  249. Array.Clear (KeyValue, 0, KeyValue.Length);
  250. KeyValue = null;
  251. }
  252. }
  253. /// <summary>
  254. /// Gets or sets the actual BlockSize
  255. /// </summary>
  256. public virtual int BlockSize {
  257. get { return this.BlockSizeValue; }
  258. set {
  259. if (IsLegalKeySize(this.LegalBlockSizesValue, value))
  260. this.BlockSizeValue = value;
  261. else
  262. throw new CryptographicException("block size not supported by algorithm");
  263. }
  264. }
  265. /// <summary>
  266. /// Gets or sets the actual FeedbackSize
  267. /// </summary>
  268. public virtual int FeedbackSize {
  269. get { return this.FeedbackSizeValue; }
  270. set {
  271. if (value > this.BlockSizeValue)
  272. throw new CryptographicException("feedback size larger than block size");
  273. else
  274. this.FeedbackSizeValue = value;
  275. }
  276. }
  277. /// <summary>
  278. /// Gets or sets the actual Initial Vector
  279. /// </summary>
  280. [MonoTODO]
  281. public virtual byte[] IV {
  282. get {
  283. if (this.IVValue == null)
  284. GenerateIV();
  285. return this.IVValue;
  286. }
  287. set {
  288. if (value == null)
  289. throw new ArgumentNullException ("tried setting initial vector to null");
  290. if (value.Length * 8 != this.BlockSizeValue)
  291. throw new CryptographicException ("IV length must match block size");
  292. this.IVValue = new byte [value.Length];
  293. System.Array.Copy (value, 0, this.IVValue, 0, value.Length);
  294. }
  295. }
  296. /// <summary>
  297. /// Gets or sets the actual key
  298. /// </summary>
  299. public virtual byte[] Key {
  300. get {
  301. if (this.KeyValue == null)
  302. GenerateKey();
  303. return this.KeyValue;
  304. }
  305. set {
  306. if (value == null)
  307. throw new ArgumentNullException ("tried setting key to null");
  308. if (!IsLegalKeySize (this.LegalKeySizesValue, value.Length * 8))
  309. throw new CryptographicException ("key size not supported by algorithm");
  310. this.KeySizeValue = value.Length * 8;
  311. this.KeyValue = new byte [value.Length];
  312. System.Array.Copy (value, 0, this.KeyValue, 0, value.Length);
  313. }
  314. }
  315. /// <summary>
  316. /// Gets or sets the actual key size in bits
  317. /// </summary>
  318. public virtual int KeySize {
  319. get { return this.KeySizeValue; }
  320. set {
  321. if (!IsLegalKeySize (this.LegalKeySizesValue, value))
  322. throw new CryptographicException ("key size not supported by algorithm");
  323. this.KeyValue = null;
  324. this.KeySizeValue = value;
  325. }
  326. }
  327. /// <summary>
  328. /// Gets all legal block sizes
  329. /// </summary>
  330. public virtual KeySizes[] LegalBlockSizes {
  331. get { return this.LegalBlockSizesValue; }
  332. }
  333. /// <summary>
  334. /// Gets all legal key sizes
  335. /// </summary>
  336. public virtual KeySizes[] LegalKeySizes {
  337. get { return this.LegalKeySizesValue; }
  338. }
  339. /// <summary>
  340. /// Gets or sets the actual cipher mode
  341. /// </summary>
  342. public virtual CipherMode Mode {
  343. get { return this.ModeValue; }
  344. set {
  345. if (Enum.IsDefined( ModeValue.GetType (), value))
  346. this.ModeValue = value;
  347. else
  348. throw new CryptographicException ("padding mode not available");
  349. }
  350. }
  351. /// <summary>
  352. /// Gets or sets the actual padding
  353. /// </summary>
  354. public virtual PaddingMode Padding {
  355. get { return this.PaddingValue; }
  356. set {
  357. if (Enum.IsDefined (PaddingValue.GetType (), value))
  358. this.PaddingValue = value;
  359. else
  360. throw new CryptographicException ("padding mode not available");
  361. }
  362. }
  363. /// <summary>
  364. /// Gets an Decryptor transform object to work with a CryptoStream
  365. /// </summary>
  366. public virtual ICryptoTransform CreateDecryptor ()
  367. {
  368. return CreateDecryptor (Key, IV);
  369. }
  370. /// <summary>
  371. /// Gets an Decryptor transform object to work with a CryptoStream
  372. /// </summary>
  373. public abstract ICryptoTransform CreateDecryptor (byte[] rgbKey, byte[] rgbIV);
  374. /// <summary>
  375. /// Gets an Encryptor transform object to work with a CryptoStream
  376. /// </summary>
  377. public virtual ICryptoTransform CreateEncryptor()
  378. {
  379. return CreateEncryptor (Key, IV);
  380. }
  381. /// <summary>
  382. /// Gets an Encryptor transform object to work with a CryptoStream
  383. /// </summary>
  384. public abstract ICryptoTransform CreateEncryptor (byte[] rgbKey, byte[] rgbIV);
  385. /// <summary>
  386. /// used to generate an inital vector if none is specified
  387. /// </summary>
  388. public abstract void GenerateIV ();
  389. /// </summary>
  390. /// used to generate a random key if none is specified
  391. /// </summary>
  392. public abstract void GenerateKey ();
  393. internal bool IsLegalKeySize (KeySizes[] LegalKeys, int Size)
  394. {
  395. foreach (KeySizes LegalKeySize in LegalKeys) {
  396. for (int i=LegalKeySize.MinSize; i<=LegalKeySize.MaxSize; i+=LegalKeySize.SkipSize) {
  397. if (i == Size)
  398. return true;
  399. }
  400. }
  401. return false;
  402. }
  403. /// <summary>
  404. /// Checks wether the given keyLength is valid for the current algorithm
  405. /// </summary>
  406. /// <param name="bitLength">the given keyLength</param>
  407. public bool ValidKeySize (int bitLength)
  408. {
  409. return IsLegalKeySize (LegalKeySizesValue, bitLength);
  410. }
  411. /// <summary>
  412. /// Creates the default implementation of the default symmetric algorithm (Rijndael).
  413. /// </summary>
  414. // LAMESPEC: Default is Rijndael - not TripleDES
  415. public static SymmetricAlgorithm Create ()
  416. {
  417. return Create ("System.Security.Cryptography.SymmetricAlgorithm");
  418. }
  419. /// <summary>
  420. /// Creates a specific implementation of the given symmetric algorithm.
  421. /// </summary>
  422. /// <param name="algName">Specifies which derived class to create</param>
  423. public static SymmetricAlgorithm Create (string algName)
  424. {
  425. return (SymmetricAlgorithm) CryptoConfig.CreateFromName (algName);
  426. }
  427. }
  428. }