ChaCha20Poly1305Cipher.cs 6.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177
  1. using System;
  2. using System.Buffers.Binary;
  3. using System.Diagnostics;
  4. using Org.BouncyCastle.Crypto.Engines;
  5. using Org.BouncyCastle.Crypto.Macs;
  6. using Org.BouncyCastle.Crypto.Parameters;
  7. using Org.BouncyCastle.Utilities;
  8. using Renci.SshNet.Common;
  9. using Renci.SshNet.Messages.Transport;
  10. namespace Renci.SshNet.Security.Cryptography.Ciphers
  11. {
  12. /// <summary>
  13. /// ChaCha20Poly1305 cipher implementation.
  14. /// <see href="https://datatracker.ietf.org/doc/html/draft-josefsson-ssh-chacha20-poly1305-openssh-00"/>.
  15. /// </summary>
  16. internal sealed class ChaCha20Poly1305Cipher : SymmetricCipher
  17. {
  18. private readonly byte[] _iv;
  19. private readonly int _aadLength;
  20. private readonly KeyParameter _aadKeyParameter;
  21. private readonly KeyParameter _keyParameter;
  22. private readonly ChaCha7539Engine _aadCipher;
  23. private readonly ChaCha7539Engine _cipher;
  24. private readonly Poly1305 _mac;
  25. /// <summary>
  26. /// Gets the minimum block size.
  27. /// </summary>
  28. public override byte MinimumSize
  29. {
  30. get
  31. {
  32. return 16;
  33. }
  34. }
  35. /// <summary>
  36. /// Gets the tag size in bytes.
  37. /// Poly1305 [Poly1305], also by Daniel Bernstein, is a one-time Carter-
  38. /// Wegman MAC that computes a 128 bit integrity tag given a message
  39. /// <see href="https://datatracker.ietf.org/doc/html/draft-josefsson-ssh-chacha20-poly1305-openssh-00#section-1"/>.
  40. /// </summary>
  41. public override int TagSize
  42. {
  43. get
  44. {
  45. return 16;
  46. }
  47. }
  48. /// <summary>
  49. /// Initializes a new instance of the <see cref="ChaCha20Poly1305Cipher"/> class.
  50. /// </summary>
  51. /// <param name="key">The key.</param>
  52. /// <param name="aadLength">The length of additional associated data.</param>
  53. public ChaCha20Poly1305Cipher(byte[] key, int aadLength)
  54. : base(key)
  55. {
  56. _iv = new byte[12];
  57. _aadLength = aadLength;
  58. _keyParameter = new KeyParameter(key, 0, 32);
  59. _cipher = new ChaCha7539Engine();
  60. if (aadLength > 0)
  61. {
  62. _aadKeyParameter = new KeyParameter(key, 32, 32);
  63. _aadCipher = new ChaCha7539Engine();
  64. }
  65. _mac = new Poly1305();
  66. }
  67. /// <summary>
  68. /// Encrypts the specified input.
  69. /// </summary>
  70. /// <param name="input">
  71. /// The input data with below format:
  72. /// <code>
  73. /// [----(offset)][----AAD----][----Plain Text----(length)]
  74. /// </code>
  75. /// </param>
  76. /// <param name="offset">The zero-based offset in <paramref name="input"/> at which to begin encrypting.</param>
  77. /// <param name="length">The number of bytes to encrypt from <paramref name="input"/>.</param>
  78. /// <returns>
  79. /// The encrypted data with below format:
  80. /// <code>
  81. /// [----Cipher AAD----][----Cipher Text----][----TAG----]
  82. /// </code>
  83. /// </returns>
  84. public override byte[] Encrypt(byte[] input, int offset, int length)
  85. {
  86. _aadCipher?.Init(forEncryption: true, new ParametersWithIV(_aadKeyParameter, _iv));
  87. _cipher.Init(forEncryption: true, new ParametersWithIV(_keyParameter, _iv));
  88. var keyStream = new byte[64];
  89. _cipher.ProcessBytes(keyStream, 0, keyStream.Length, keyStream, 0);
  90. _mac.Init(new KeyParameter(keyStream, 0, 32));
  91. var output = new byte[length + TagSize];
  92. _aadCipher?.ProcessBytes(input, offset, _aadLength, output, 0);
  93. _cipher.ProcessBytes(input, offset + _aadLength, length - _aadLength, output, _aadLength);
  94. _mac.BlockUpdate(output, 0, length);
  95. _ = _mac.DoFinal(output, length);
  96. return output;
  97. }
  98. /// <summary>
  99. /// Decrypts the AAD.
  100. /// </summary>
  101. /// <param name="input">The encrypted AAD.</param>
  102. /// <returns>The decrypted AAD.</returns>
  103. public override byte[] Decrypt(byte[] input)
  104. {
  105. Debug.Assert(_aadCipher != null, "The aadCipher must not be null");
  106. _aadCipher.Init(forEncryption: false, new ParametersWithIV(_aadKeyParameter, _iv));
  107. var output = new byte[input.Length];
  108. _aadCipher.ProcessBytes(input, 0, input.Length, output, 0);
  109. return output;
  110. }
  111. /// <summary>
  112. /// Decrypts the specified input.
  113. /// </summary>
  114. /// <param name="input">
  115. /// The input data with below format:
  116. /// <code>
  117. /// [----][----Cipher AAD----(offset)][----Cipher Text----(length)][----TAG----]
  118. /// </code>
  119. /// </param>
  120. /// <param name="offset">The zero-based offset in <paramref name="input"/> at which to begin decrypting and authenticating.</param>
  121. /// <param name="length">The number of bytes to decrypt and authenticate from <paramref name="input"/>.</param>
  122. /// <returns>
  123. /// The decrypted data with below format:
  124. /// <code>
  125. /// [----Plain Text----]
  126. /// </code>
  127. /// </returns>
  128. public override byte[] Decrypt(byte[] input, int offset, int length)
  129. {
  130. Debug.Assert(offset >= _aadLength, "The offset must be greater than or equals to aad length");
  131. _cipher.Init(forEncryption: false, new ParametersWithIV(_keyParameter, _iv));
  132. var keyStream = new byte[64];
  133. _cipher.ProcessBytes(keyStream, 0, keyStream.Length, keyStream, 0);
  134. _mac.Init(new KeyParameter(keyStream, 0, 32));
  135. var tag = new byte[TagSize];
  136. _mac.BlockUpdate(input, offset - _aadLength, length + _aadLength);
  137. _ = _mac.DoFinal(tag, 0);
  138. if (!Arrays.FixedTimeEquals(TagSize, tag, 0, input, offset + length))
  139. {
  140. throw new SshConnectionException("MAC error", DisconnectReason.MacError);
  141. }
  142. var output = new byte[length];
  143. _cipher.ProcessBytes(input, offset, length, output, 0);
  144. return output;
  145. }
  146. internal override void SetSequenceNumber(uint sequenceNumber)
  147. {
  148. BinaryPrimitives.WriteUInt64BigEndian(_iv.AsSpan(4), sequenceNumber);
  149. }
  150. }
  151. }