ChaCha20Poly1305Cipher.cs 6.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157
  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 ChaCha7539Engine _aadCipher = new ChaCha7539Engine();
  19. private readonly ChaCha7539Engine _cipher = new ChaCha7539Engine();
  20. private readonly Poly1305 _mac = new Poly1305();
  21. /// <summary>
  22. /// Gets the minimun block size.
  23. /// </summary>
  24. public override byte MinimumSize
  25. {
  26. get
  27. {
  28. return 16;
  29. }
  30. }
  31. /// <summary>
  32. /// Gets the tag size in bytes.
  33. /// Poly1305 [Poly1305], also by Daniel Bernstein, is a one-time Carter-
  34. /// Wegman MAC that computes a 128 bit integrity tag given a message
  35. /// <see href="https://datatracker.ietf.org/doc/html/draft-josefsson-ssh-chacha20-poly1305-openssh-00#section-1"/>.
  36. /// </summary>
  37. public override int TagSize
  38. {
  39. get
  40. {
  41. return 16;
  42. }
  43. }
  44. /// <summary>
  45. /// Initializes a new instance of the <see cref="ChaCha20Poly1305Cipher"/> class.
  46. /// </summary>
  47. /// <param name="key">The key.</param>
  48. public ChaCha20Poly1305Cipher(byte[] key)
  49. : base(key)
  50. {
  51. }
  52. /// <summary>
  53. /// Encrypts the specified input.
  54. /// </summary>
  55. /// <param name="input">
  56. /// The input data with below format:
  57. /// <code>
  58. /// [outbound sequence field][packet length field][padding length field sz][payload][random paddings]
  59. /// [----4 bytes----(offset)][------4 bytes------][----------------Plain Text---------------(length)]
  60. /// </code>
  61. /// </param>
  62. /// <param name="offset">The zero-based offset in <paramref name="input"/> at which to begin encrypting.</param>
  63. /// <param name="length">The number of bytes to encrypt from <paramref name="input"/>.</param>
  64. /// <returns>
  65. /// The encrypted data with below format:
  66. /// <code>
  67. /// [packet length field][padding length field sz][payload][random paddings][Authenticated TAG]
  68. /// [------4 bytes------][------------------Cipher Text--------------------][-------TAG-------]
  69. /// </code>
  70. /// </returns>
  71. public override byte[] Encrypt(byte[] input, int offset, int length)
  72. {
  73. var output = new byte[length + TagSize];
  74. _aadCipher.ProcessBytes(input, offset, 4, output, 0);
  75. _cipher.ProcessBytes(input, offset + 4, length - 4, output, 4);
  76. _mac.BlockUpdate(output, 0, length);
  77. _ = _mac.DoFinal(output, length);
  78. return output;
  79. }
  80. /// <summary>
  81. /// Decrypts the first block which is packet length field.
  82. /// </summary>
  83. /// <param name="input">The encrypted packet length field.</param>
  84. /// <returns>The decrypted packet length field.</returns>
  85. public override byte[] Decrypt(byte[] input)
  86. {
  87. var output = new byte[input.Length];
  88. _aadCipher.ProcessBytes(input, 0, input.Length, output, 0);
  89. return output;
  90. }
  91. /// <summary>
  92. /// Decrypts the specified input.
  93. /// </summary>
  94. /// <param name="input">
  95. /// The input data with below format:
  96. /// <code>
  97. /// [inbound sequence field][packet length field][padding length field sz][payload][random paddings][Authenticated TAG]
  98. /// [--------4 bytes-------][--4 bytes--(offset)][--------------Cipher Text----------------(length)][-------TAG-------]
  99. /// </code>
  100. /// </param>
  101. /// <param name="offset">The zero-based offset in <paramref name="input"/> at which to begin decrypting and authenticating.</param>
  102. /// <param name="length">The number of bytes to decrypt and authenticate from <paramref name="input"/>.</param>
  103. /// <returns>
  104. /// The decrypted data with below format:
  105. /// <code>
  106. /// [padding length field sz][payload][random paddings]
  107. /// [--------------------Plain Text-------------------]
  108. /// </code>
  109. /// </returns>
  110. public override byte[] Decrypt(byte[] input, int offset, int length)
  111. {
  112. Debug.Assert(offset == 8, "The offset must be 8");
  113. var tag = new byte[TagSize];
  114. _mac.BlockUpdate(input, offset - 4, length + 4);
  115. _ = _mac.DoFinal(tag, 0);
  116. if (!Arrays.FixedTimeEquals(TagSize, tag, 0, input, offset + length))
  117. {
  118. throw new SshConnectionException("MAC error", DisconnectReason.MacError);
  119. }
  120. var output = new byte[length];
  121. _cipher.ProcessBytes(input, offset, length, output, 0);
  122. return output;
  123. }
  124. internal override void SetSequenceNumber(uint sequenceNumber)
  125. {
  126. var iv = new byte[12];
  127. BinaryPrimitives.WriteUInt64BigEndian(iv.AsSpan(4), sequenceNumber);
  128. // ChaCha20 encryption and decryption is completely
  129. // symmetrical, so the 'forEncryption' is
  130. // irrelevant. (Like 90% of stream ciphers)
  131. _aadCipher.Init(forEncryption: true, new ParametersWithIV(new KeyParameter(Key, 32, 32), iv));
  132. _cipher.Init(forEncryption: true, new ParametersWithIV(new KeyParameter(Key, 0, 32), iv));
  133. var keyStream = new byte[64];
  134. _cipher.ProcessBytes(keyStream, 0, keyStream.Length, keyStream, 0);
  135. _mac.Init(new KeyParameter(keyStream, 0, 32));
  136. }
  137. }
  138. }