| | | 1 | | // Based on Noise.NET by Nemanja Mijailovic https://github.com/Metalnem/noise |
| | | 2 | | |
| | | 3 | | using System.Buffers.Binary; |
| | | 4 | | using System.Diagnostics; |
| | | 5 | | using System.Security.Cryptography; |
| | | 6 | | |
| | | 7 | | namespace NLightning.Infrastructure.Crypto.Ciphers; |
| | | 8 | | |
| | | 9 | | using Domain.Crypto.Constants; |
| | | 10 | | using Factories; |
| | | 11 | | using Interfaces; |
| | | 12 | | |
| | | 13 | | /// <summary> |
| | | 14 | | /// AEAD_CHACHA20_POLY1305 from <see href="https://tools.ietf.org/html/rfc7539">RFC 7539</see>. |
| | | 15 | | /// The 96-bit nonce is formed by encoding 32 bits of zeros followed by little-endian encoding of n. |
| | | 16 | | /// </summary> |
| | | 17 | | public sealed class ChaCha20Poly1305 : IDisposable |
| | | 18 | | { |
| | | 19 | | private readonly ICryptoProvider _cryptoProvider; |
| | | 20 | | |
| | 200 | 21 | | public ChaCha20Poly1305() |
| | | 22 | | { |
| | 200 | 23 | | _cryptoProvider = CryptoFactory.GetCryptoProvider(); |
| | 200 | 24 | | } |
| | | 25 | | |
| | | 26 | | /// <summary> |
| | | 27 | | /// Encrypts plaintext using the cipher key of 32 bytes and an 8-byte unsigned integer publicNonce which must be |
| | | 28 | | /// unique for the key. Writes the result into ciphertext parameter and returns the number of bytes written. |
| | | 29 | | /// Encryption must be done with an "AEAD" encryption mode with the authenticationData and results in a ciphertext |
| | | 30 | | /// that is the same size as the plaintext plus 16 bytes for authentication data. |
| | | 31 | | /// </summary> |
| | | 32 | | public int Encrypt(ReadOnlySpan<byte> key, ulong publicNonce, ReadOnlySpan<byte> authenticationData, |
| | | 33 | | ReadOnlySpan<byte> plaintext, Span<byte> ciphertext) |
| | | 34 | | { |
| | | 35 | | Debug.Assert(key.Length == CryptoConstants.PrivkeyLen); |
| | | 36 | | Debug.Assert(ciphertext.Length >= plaintext.Length + CryptoConstants.Chacha20Poly1305TagLen); |
| | | 37 | | |
| | 8148 | 38 | | Span<byte> nonce = stackalloc byte[CryptoConstants.Chacha20Poly1305NonceLen]; |
| | 8148 | 39 | | BinaryPrimitives.WriteUInt64LittleEndian(nonce[4..], publicNonce); |
| | | 40 | | |
| | 8148 | 41 | | var result = _cryptoProvider.AeadChaCha20Poly1305IetfEncrypt(key, nonce, null, authenticationData, plaintext, |
| | 8148 | 42 | | ciphertext, out var length); |
| | | 43 | | |
| | 8148 | 44 | | if (result != 0) |
| | | 45 | | { |
| | 0 | 46 | | throw new CryptographicException("Encryption failed."); |
| | | 47 | | } |
| | | 48 | | |
| | | 49 | | Debug.Assert(length == plaintext.Length + CryptoConstants.Chacha20Poly1305TagLen); |
| | 8148 | 50 | | return (int)length; |
| | | 51 | | } |
| | | 52 | | |
| | | 53 | | /// <summary> |
| | | 54 | | /// Decrypts ciphertext using a cipher key of 32 bytes, an 8-byte unsigned integer publicNonce, and |
| | | 55 | | /// authenticationData. Reads the result into plaintext parameter and returns the number of bytes read, unless |
| | | 56 | | /// authentication fails, in which case an error is signaled to the caller. |
| | | 57 | | /// </summary> |
| | | 58 | | public int Decrypt(ReadOnlySpan<byte> key, ulong publicNonce, ReadOnlySpan<byte> authenticationData, |
| | | 59 | | ReadOnlySpan<byte> ciphertext, Span<byte> plaintext) |
| | | 60 | | { |
| | | 61 | | Debug.Assert(key.Length == CryptoConstants.PrivkeyLen); |
| | | 62 | | Debug.Assert(ciphertext.Length >= CryptoConstants.Chacha20Poly1305TagLen); |
| | | 63 | | Debug.Assert(plaintext.Length >= ciphertext.Length - CryptoConstants.Chacha20Poly1305TagLen); |
| | | 64 | | |
| | 8140 | 65 | | Span<byte> nonce = stackalloc byte[CryptoConstants.Chacha20Poly1305NonceLen]; |
| | 8140 | 66 | | BinaryPrimitives.WriteUInt64LittleEndian(nonce[4..], publicNonce); |
| | | 67 | | |
| | 8140 | 68 | | var result = _cryptoProvider.AeadChaCha20Poly1305IetfDecrypt(key, nonce, null, authenticationData, |
| | 8140 | 69 | | ciphertext, plaintext, out var length); |
| | | 70 | | |
| | 8130 | 71 | | if (result != 0) |
| | | 72 | | { |
| | 10 | 73 | | throw new CryptographicException("Decryption failed."); |
| | | 74 | | } |
| | | 75 | | |
| | | 76 | | Debug.Assert(length == ciphertext.Length - CryptoConstants.Chacha20Poly1305TagLen); |
| | 8120 | 77 | | return (int)length; |
| | | 78 | | } |
| | | 79 | | |
| | | 80 | | public void Dispose() |
| | | 81 | | { |
| | 152 | 82 | | _cryptoProvider.Dispose(); |
| | 152 | 83 | | } |
| | | 84 | | } |