SymmetricAlgorithm.cs 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568
  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: Builds CBC and CFB 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 byte[] workBuff;
  28. private byte[] workout;
  29. private int FeedBackByte;
  30. private int FeedBackIter;
  31. private bool m_disposed = false;
  32. public SymmetricTransform (SymmetricAlgorithm symmAlgo, bool encryption, byte[] rgbIV)
  33. {
  34. algo = symmAlgo;
  35. encrypt = encryption;
  36. BlockSizeByte = (algo.BlockSize >> 3);
  37. // mode buffers
  38. temp = new byte [BlockSizeByte];
  39. Array.Copy (rgbIV, 0, temp, 0, BlockSizeByte);
  40. temp2 = new byte [BlockSizeByte];
  41. FeedBackByte = (algo.FeedbackSize >> 3);
  42. FeedBackIter = (int) BlockSizeByte / FeedBackByte;
  43. // transform buffers
  44. workBuff = new byte [BlockSizeByte];
  45. workout = new byte [BlockSizeByte];
  46. }
  47. ~SymmetricTransform ()
  48. {
  49. Dispose (false);
  50. }
  51. void IDisposable.Dispose ()
  52. {
  53. Dispose (true);
  54. GC.SuppressFinalize (this); // Finalization is now unnecessary
  55. }
  56. // MUST be overriden by classes using unmanaged ressources
  57. // the override method must call the base class
  58. protected void Dispose (bool disposing)
  59. {
  60. if (!m_disposed) {
  61. if (disposing) {
  62. // dispose managed object: zeroize and free
  63. Array.Clear (temp, 0, BlockSizeByte);
  64. temp = null;
  65. Array.Clear (temp2, 0, BlockSizeByte);
  66. temp2 = null;
  67. }
  68. m_disposed = true;
  69. }
  70. }
  71. public virtual bool CanTransformMultipleBlocks {
  72. get { return true; }
  73. }
  74. public bool CanReuseTransform {
  75. get { return false; }
  76. }
  77. public virtual int InputBlockSize {
  78. get { return BlockSizeByte; }
  79. }
  80. public virtual int OutputBlockSize {
  81. get { return BlockSizeByte; }
  82. }
  83. // note: Each block MUST be BlockSizeValue in size!!!
  84. // i.e. Any padding must be done before calling this method
  85. protected void Transform (byte[] input, byte[] output)
  86. {
  87. switch (algo.Mode) {
  88. case CipherMode.ECB:
  89. ECB (input, output);
  90. break;
  91. case CipherMode.CBC:
  92. CBC (input, output);
  93. break;
  94. case CipherMode.CFB:
  95. CFB (input, output);
  96. break;
  97. case CipherMode.OFB:
  98. OFB (input, output);
  99. break;
  100. case CipherMode.CTS:
  101. CTS (input, output);
  102. break;
  103. default:
  104. throw new NotImplementedException ("Unkown CipherMode" + algo.Mode.ToString ());
  105. }
  106. }
  107. // Electronic Code Book (ECB)
  108. protected abstract void ECB (byte[] input, byte[] output);
  109. // Cipher-Block-Chaining (CBC)
  110. protected virtual void CBC (byte[] input, byte[] output)
  111. {
  112. if (encrypt) {
  113. for (int i = 0; i < BlockSizeByte; i++)
  114. temp[i] ^= input[i];
  115. ECB (temp, output);
  116. Array.Copy (output, 0, temp, 0, BlockSizeByte);
  117. }
  118. else {
  119. Array.Copy (input, 0, temp2, 0, BlockSizeByte);
  120. ECB (input, output);
  121. for (int i = 0; i < BlockSizeByte; i++)
  122. output[i] ^= temp[i];
  123. Array.Copy (temp2, 0, temp, 0, BlockSizeByte);
  124. }
  125. }
  126. // Cipher-FeedBack (CFB)
  127. protected virtual void CFB (byte[] input, byte[] output)
  128. {
  129. if (encrypt) {
  130. for (int x = 0; x < FeedBackIter; x++) {
  131. // temp is first initialized with the IV
  132. ECB (temp, temp2);
  133. for (int i = 0; i < FeedBackByte; i++)
  134. output[i + x] = (byte)(temp2[i] ^ input[i + x]);
  135. Array.Copy (temp, FeedBackByte, temp, 0, BlockSizeByte - FeedBackByte);
  136. Array.Copy (output, x, temp, BlockSizeByte - FeedBackByte, FeedBackByte);
  137. }
  138. }
  139. else {
  140. for (int x = 0; x < FeedBackIter; x++) {
  141. // we do not really decrypt this data!
  142. encrypt = true;
  143. // temp is first initialized with the IV
  144. ECB (temp, temp2);
  145. encrypt = false;
  146. Array.Copy (temp, FeedBackByte, temp, 0, BlockSizeByte - FeedBackByte);
  147. Array.Copy (input, x, temp, BlockSizeByte - FeedBackByte, FeedBackByte);
  148. for (int i = 0; i < FeedBackByte; i++)
  149. output[i + x] = (byte)(temp2[i] ^ input[i + x]);
  150. }
  151. }
  152. }
  153. // Output-FeedBack (OFB)
  154. protected virtual void OFB (byte[] input, byte[] output)
  155. {
  156. throw new NotImplementedException ("OFB not yet supported");
  157. }
  158. // Cipher Text Stealing (CTS)
  159. protected virtual void CTS (byte[] input, byte[] output)
  160. {
  161. throw new NotImplementedException ("CTS not yet supported");
  162. }
  163. // this method may get called MANY times so this is the one to optimize
  164. public virtual int TransformBlock (byte [] inputBuffer, int inputOffset, int inputCount, byte [] outputBuffer, int outputOffset)
  165. {
  166. if (m_disposed)
  167. throw new ObjectDisposedException ("Object is disposed");
  168. if (outputOffset + inputCount > outputBuffer.Length)
  169. throw new CryptographicException ("Insufficient output buffer size.");
  170. int offs = inputOffset;
  171. int full;
  172. // this way we don't do a modulo every time we're called
  173. // and we may save a division
  174. if (inputCount != BlockSizeByte) {
  175. if ((inputCount % BlockSizeByte) != 0)
  176. throw new CryptographicException ("Invalid input block size.");
  177. full = inputCount / BlockSizeByte;
  178. }
  179. else
  180. full = 1;
  181. int total = 0;
  182. for (int i = 0; i < full; i++) {
  183. Array.Copy (inputBuffer, offs, workBuff, 0, BlockSizeByte);
  184. Transform (workBuff, workout);
  185. Array.Copy (workout, 0, outputBuffer, outputOffset, BlockSizeByte);
  186. offs += BlockSizeByte;
  187. outputOffset += BlockSizeByte;
  188. total += BlockSizeByte;
  189. }
  190. return total;
  191. }
  192. private byte[] FinalEncrypt (byte [] inputBuffer, int inputOffset, int inputCount)
  193. {
  194. // are there still full block to process ?
  195. int full = (inputCount / BlockSizeByte) * BlockSizeByte;
  196. int rem = inputCount - full;
  197. int total = full;
  198. // we need to add an extra block if...
  199. // a. the last block isn't complate (partial);
  200. // b. the last block is complete but we use padding
  201. if ((rem > 0) || (algo.Padding != PaddingMode.None))
  202. total += BlockSizeByte;
  203. byte[] res = new byte [total];
  204. // process all blocks except the last (final) block
  205. while (total > BlockSizeByte) {
  206. TransformBlock (inputBuffer, inputOffset, BlockSizeByte, res, inputOffset);
  207. inputOffset += BlockSizeByte;
  208. total -= BlockSizeByte;
  209. }
  210. // now we only have a single last block to encrypt
  211. int padding = BlockSizeByte - rem;
  212. switch (algo.Padding) {
  213. case PaddingMode.None:
  214. break;
  215. case PaddingMode.PKCS7:
  216. for (int i = BlockSizeByte; --i >= (BlockSizeByte - padding);)
  217. res [i] = (byte) padding;
  218. break;
  219. case PaddingMode.Zeros:
  220. for (int i = BlockSizeByte; --i >= (BlockSizeByte - padding);)
  221. res [i] = 0;
  222. break;
  223. }
  224. Array.Copy (inputBuffer, inputOffset, res, full, rem);
  225. // the last padded block will be transformed in-place
  226. TransformBlock (res, full, BlockSizeByte, res, full);
  227. return res;
  228. }
  229. private byte[] FinalDecrypt (byte [] inputBuffer, int inputOffset, int inputCount)
  230. {
  231. if ((inputCount % BlockSizeByte) > 0)
  232. throw new CryptographicException ("Invalid input block size.");
  233. int total = inputCount;
  234. byte[] res = new byte [total];
  235. while (inputCount > 0) {
  236. TransformBlock (inputBuffer, inputOffset, BlockSizeByte, res, inputOffset);
  237. inputOffset += BlockSizeByte;
  238. inputCount -= BlockSizeByte;
  239. }
  240. switch (algo.Padding) {
  241. case PaddingMode.None:
  242. break;
  243. case PaddingMode.PKCS7:
  244. total -= res [total - 1];
  245. break;
  246. case PaddingMode.Zeros:
  247. // TODO
  248. break;
  249. }
  250. // return output without padding
  251. byte[] data = new byte [total];
  252. Array.Copy (res, 0, data, 0, total);
  253. // zeroize decrypted data (copy with padding)
  254. Array.Clear (res, 0, res.Length);
  255. return data;
  256. }
  257. public virtual byte [] TransformFinalBlock (byte [] inputBuffer, int inputOffset, int inputCount)
  258. {
  259. if (m_disposed)
  260. throw new ObjectDisposedException ("Object is disposed");
  261. if (encrypt)
  262. return FinalEncrypt (inputBuffer, inputOffset, inputCount);
  263. else
  264. return FinalDecrypt (inputBuffer, inputOffset, inputCount);
  265. }
  266. }
  267. /// <summary>
  268. /// Abstract base class for all cryptographic symmetric algorithms.
  269. /// Available algorithms include:
  270. /// DES, RC2, Rijndael, TripleDES
  271. /// </summary>
  272. public abstract class SymmetricAlgorithm : IDisposable {
  273. protected int BlockSizeValue; // The block size of the cryptographic operation in bits.
  274. protected int FeedbackSizeValue; // The feedback size of the cryptographic operation in bits.
  275. protected byte[] IVValue; // The initialization vector ( IV) for the symmetric algorithm.
  276. protected int KeySizeValue; // The size of the secret key used by the symmetric algorithm in bits.
  277. protected byte[] KeyValue; // The secret key for the symmetric algorithm.
  278. protected KeySizes[] LegalBlockSizesValue; // Specifies the block sizes that are supported by the symmetric algorithm.
  279. protected KeySizes[] LegalKeySizesValue; // Specifies the key sizes that are supported by the symmetric algorithm.
  280. protected CipherMode ModeValue; // Represents the cipher mode used in the symmetric algorithm.
  281. protected PaddingMode PaddingValue; // Represents the padding mode used in the symmetric algorithm.
  282. private bool m_disposed;
  283. /// <summary>
  284. /// Called from constructor of derived class.
  285. /// </summary>
  286. public SymmetricAlgorithm ()
  287. {
  288. ModeValue = CipherMode.CBC;
  289. PaddingValue = PaddingMode.PKCS7;
  290. m_disposed = false;
  291. }
  292. /// <summary>
  293. /// Called from constructor of derived class.
  294. /// </summary>
  295. ~SymmetricAlgorithm ()
  296. {
  297. Dispose (false);
  298. }
  299. public void Clear()
  300. {
  301. Dispose (true);
  302. }
  303. void IDisposable.Dispose ()
  304. {
  305. Dispose (true);
  306. GC.SuppressFinalize (this); // Finalization is now unnecessary
  307. }
  308. protected virtual void Dispose (bool disposing)
  309. {
  310. if (!m_disposed) {
  311. // always zeroize keys
  312. if (KeyValue != null) {
  313. // Zeroize the secret key and free
  314. Array.Clear (KeyValue, 0, KeyValue.Length);
  315. KeyValue = null;
  316. }
  317. // dispose unmanaged managed objects
  318. if (disposing) {
  319. // dispose managed objects
  320. }
  321. m_disposed = true;
  322. }
  323. }
  324. /// <summary>
  325. /// Gets or sets the actual BlockSize
  326. /// </summary>
  327. public virtual int BlockSize {
  328. get { return this.BlockSizeValue; }
  329. set {
  330. if (IsLegalKeySize(this.LegalBlockSizesValue, value))
  331. this.BlockSizeValue = value;
  332. else
  333. throw new CryptographicException("block size not supported by algorithm");
  334. }
  335. }
  336. /// <summary>
  337. /// Gets or sets the actual FeedbackSize
  338. /// </summary>
  339. public virtual int FeedbackSize {
  340. get { return this.FeedbackSizeValue; }
  341. set {
  342. if (value > this.BlockSizeValue)
  343. throw new CryptographicException("feedback size larger than block size");
  344. else
  345. this.FeedbackSizeValue = value;
  346. }
  347. }
  348. /// <summary>
  349. /// Gets or sets the actual Initial Vector
  350. /// </summary>
  351. public virtual byte[] IV {
  352. get {
  353. if (this.IVValue == null)
  354. GenerateIV();
  355. return this.IVValue;
  356. }
  357. set {
  358. if (value == null)
  359. throw new ArgumentNullException ("tried setting initial vector to null");
  360. if (value.Length * 8 != this.BlockSizeValue)
  361. throw new CryptographicException ("IV length must match block size");
  362. this.IVValue = new byte [value.Length];
  363. Array.Copy (value, 0, this.IVValue, 0, value.Length);
  364. }
  365. }
  366. /// <summary>
  367. /// Gets or sets the actual key
  368. /// </summary>
  369. public virtual byte[] Key {
  370. get {
  371. if (this.KeyValue == null)
  372. GenerateKey();
  373. return this.KeyValue;
  374. }
  375. set {
  376. if (value == null)
  377. throw new ArgumentNullException ("tried setting key to null");
  378. if (!IsLegalKeySize (this.LegalKeySizesValue, value.Length * 8))
  379. throw new CryptographicException ("key size not supported by algorithm");
  380. this.KeySizeValue = value.Length * 8;
  381. this.KeyValue = new byte [value.Length];
  382. Array.Copy (value, 0, this.KeyValue, 0, value.Length);
  383. }
  384. }
  385. /// <summary>
  386. /// Gets or sets the actual key size in bits
  387. /// </summary>
  388. public virtual int KeySize {
  389. get { return this.KeySizeValue; }
  390. set {
  391. if (!IsLegalKeySize (this.LegalKeySizesValue, value))
  392. throw new CryptographicException ("key size not supported by algorithm");
  393. this.KeyValue = null;
  394. this.KeySizeValue = value;
  395. }
  396. }
  397. /// <summary>
  398. /// Gets all legal block sizes
  399. /// </summary>
  400. public virtual KeySizes[] LegalBlockSizes {
  401. get { return this.LegalBlockSizesValue; }
  402. }
  403. /// <summary>
  404. /// Gets all legal key sizes
  405. /// </summary>
  406. public virtual KeySizes[] LegalKeySizes {
  407. get { return this.LegalKeySizesValue; }
  408. }
  409. /// <summary>
  410. /// Gets or sets the actual cipher mode
  411. /// </summary>
  412. public virtual CipherMode Mode {
  413. get { return this.ModeValue; }
  414. set {
  415. if (Enum.IsDefined( ModeValue.GetType (), value))
  416. this.ModeValue = value;
  417. else
  418. throw new CryptographicException ("padding mode not available");
  419. }
  420. }
  421. /// <summary>
  422. /// Gets or sets the actual padding
  423. /// </summary>
  424. public virtual PaddingMode Padding {
  425. get { return this.PaddingValue; }
  426. set {
  427. if (Enum.IsDefined (PaddingValue.GetType (), value))
  428. this.PaddingValue = value;
  429. else
  430. throw new CryptographicException ("padding mode not available");
  431. }
  432. }
  433. /// <summary>
  434. /// Gets an Decryptor transform object to work with a CryptoStream
  435. /// </summary>
  436. public virtual ICryptoTransform CreateDecryptor ()
  437. {
  438. return CreateDecryptor (Key, IV);
  439. }
  440. /// <summary>
  441. /// Gets an Decryptor transform object to work with a CryptoStream
  442. /// </summary>
  443. public abstract ICryptoTransform CreateDecryptor (byte[] rgbKey, byte[] rgbIV);
  444. /// <summary>
  445. /// Gets an Encryptor transform object to work with a CryptoStream
  446. /// </summary>
  447. public virtual ICryptoTransform CreateEncryptor()
  448. {
  449. return CreateEncryptor (Key, IV);
  450. }
  451. /// <summary>
  452. /// Gets an Encryptor transform object to work with a CryptoStream
  453. /// </summary>
  454. public abstract ICryptoTransform CreateEncryptor (byte[] rgbKey, byte[] rgbIV);
  455. /// <summary>
  456. /// used to generate an inital vector if none is specified
  457. /// </summary>
  458. public abstract void GenerateIV ();
  459. /// </summary>
  460. /// used to generate a random key if none is specified
  461. /// </summary>
  462. public abstract void GenerateKey ();
  463. internal bool IsLegalKeySize (KeySizes[] LegalKeys, int Size)
  464. {
  465. foreach (KeySizes LegalKeySize in LegalKeys) {
  466. for (int i=LegalKeySize.MinSize; i<=LegalKeySize.MaxSize; i+=LegalKeySize.SkipSize) {
  467. if (i == Size)
  468. return true;
  469. }
  470. }
  471. return false;
  472. }
  473. /// <summary>
  474. /// Checks wether the given keyLength is valid for the current algorithm
  475. /// </summary>
  476. /// <param name="bitLength">the given keyLength</param>
  477. public bool ValidKeySize (int bitLength)
  478. {
  479. return IsLegalKeySize (LegalKeySizesValue, bitLength);
  480. }
  481. /// <summary>
  482. /// Creates the default implementation of the default symmetric algorithm (Rijndael).
  483. /// </summary>
  484. // LAMESPEC: Default is Rijndael - not TripleDES
  485. public static SymmetricAlgorithm Create ()
  486. {
  487. return Create ("System.Security.Cryptography.SymmetricAlgorithm");
  488. }
  489. /// <summary>
  490. /// Creates a specific implementation of the given symmetric algorithm.
  491. /// </summary>
  492. /// <param name="algName">Specifies which derived class to create</param>
  493. public static SymmetricAlgorithm Create (string algName)
  494. {
  495. return (SymmetricAlgorithm) CryptoConfig.CreateFromName (algName);
  496. }
  497. }
  498. }