TripleDesCipher.BclImpl.cs 5.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130
  1. using System;
  2. using System.Security.Cryptography;
  3. using Renci.SshNet.Common;
  4. namespace Renci.SshNet.Security.Cryptography.Ciphers
  5. {
  6. public partial class TripleDesCipher
  7. {
  8. private sealed class BclImpl : BlockCipher, IDisposable
  9. {
  10. private readonly TripleDES _des;
  11. private readonly ICryptoTransform _encryptor;
  12. private readonly ICryptoTransform _decryptor;
  13. public BclImpl(
  14. byte[] key,
  15. byte[] iv,
  16. System.Security.Cryptography.CipherMode mode,
  17. PaddingMode padding)
  18. : base(key, 8, mode: null, padding: null)
  19. {
  20. var des = TripleDES.Create();
  21. des.FeedbackSize = 64; // We use CFB8
  22. des.Key = Key;
  23. des.IV = iv.Take(8);
  24. des.Mode = mode;
  25. des.Padding = padding;
  26. _des = des;
  27. _encryptor = _des.CreateEncryptor();
  28. _decryptor = _des.CreateDecryptor();
  29. }
  30. public override byte[] Encrypt(byte[] input, int offset, int length)
  31. {
  32. if (_des.Padding != PaddingMode.None)
  33. {
  34. return _encryptor.TransformFinalBlock(input, offset, length);
  35. }
  36. var paddingLength = 0;
  37. if (length % BlockSize > 0)
  38. {
  39. if (_des.Mode is System.Security.Cryptography.CipherMode.CFB or System.Security.Cryptography.CipherMode.OFB)
  40. {
  41. // Manually pad the input for cfb and ofb cipher mode as BCL doesn't support partial block.
  42. // See https://github.com/dotnet/runtime/blob/e7d837da5b1aacd9325a8b8f2214cfaf4d3f0ff6/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/SymmetricPadding.cs#L20-L21
  43. paddingLength = BlockSize - (length % BlockSize);
  44. input = input.Take(offset, length);
  45. length += paddingLength;
  46. Array.Resize(ref input, length);
  47. offset = 0;
  48. }
  49. }
  50. // Otherwise, (the most important case) assume this instance is
  51. // used for one direction of an SSH connection, whereby the
  52. // encrypted data in all packets are considered a single data
  53. // stream i.e. we do not want to reset the state between calls to Encrypt.
  54. var output = new byte[length];
  55. _ = _encryptor.TransformBlock(input, offset, length, output, 0);
  56. if (paddingLength > 0)
  57. {
  58. // Manually unpad the output.
  59. Array.Resize(ref output, output.Length - paddingLength);
  60. }
  61. return output;
  62. }
  63. public override byte[] Decrypt(byte[] input, int offset, int length)
  64. {
  65. if (_des.Padding != PaddingMode.None)
  66. {
  67. // If padding has been specified, call TransformFinalBlock to apply
  68. // the padding and reset the state.
  69. return _decryptor.TransformFinalBlock(input, offset, length);
  70. }
  71. var paddingLength = 0;
  72. if (length % BlockSize > 0)
  73. {
  74. if (_des.Mode is System.Security.Cryptography.CipherMode.CFB or System.Security.Cryptography.CipherMode.OFB)
  75. {
  76. // Manually pad the input for cfb and ofb cipher mode as BCL doesn't support partial block.
  77. // See https://github.com/dotnet/runtime/blob/e7d837da5b1aacd9325a8b8f2214cfaf4d3f0ff6/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/SymmetricPadding.cs#L20-L21
  78. paddingLength = BlockSize - (length % BlockSize);
  79. input = input.Take(offset, length);
  80. length += paddingLength;
  81. Array.Resize(ref input, length);
  82. offset = 0;
  83. }
  84. }
  85. // Otherwise, (the most important case) assume this instance is
  86. // used for one direction of an SSH connection, whereby the
  87. // encrypted data in all packets are considered a single data
  88. // stream i.e. we do not want to reset the state between calls to Encrypt.
  89. var output = new byte[length];
  90. _ = _decryptor.TransformBlock(input, offset, length, output, 0);
  91. if (paddingLength > 0)
  92. {
  93. // Manually unpad the output.
  94. Array.Resize(ref output, output.Length - paddingLength);
  95. }
  96. return output;
  97. }
  98. public override int EncryptBlock(byte[] inputBuffer, int inputOffset, int inputCount, byte[] outputBuffer, int outputOffset)
  99. {
  100. throw new NotImplementedException($"Invalid usage of {nameof(EncryptBlock)}.");
  101. }
  102. public override int DecryptBlock(byte[] inputBuffer, int inputOffset, int inputCount, byte[] outputBuffer, int outputOffset)
  103. {
  104. throw new NotImplementedException($"Invalid usage of {nameof(DecryptBlock)}.");
  105. }
  106. public void Dispose()
  107. {
  108. _des.Dispose();
  109. _encryptor.Dispose();
  110. _decryptor.Dispose();
  111. }
  112. }
  113. }
  114. }