| | 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 | |
|
| 192 | 21 | | public ChaCha20Poly1305() |
| | 22 | | { |
| 192 | 23 | | _cryptoProvider = CryptoFactory.GetCryptoProvider(); |
| 192 | 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.PRIVKEY_LEN); |
| | 36 | | Debug.Assert(ciphertext.Length >= plaintext.Length + CryptoConstants.CHACHA20_POLY1305_TAG_LEN); |
| | 37 | |
|
| 8144 | 38 | | Span<byte> nonce = stackalloc byte[CryptoConstants.CHACHA20_POLY1305_NONCE_LEN]; |
| 8144 | 39 | | BinaryPrimitives.WriteUInt64LittleEndian(nonce[4..], publicNonce); |
| | 40 | |
|
| 8144 | 41 | | var result = _cryptoProvider.AeadChaCha20Poly1305IetfEncrypt(key, nonce, null, authenticationData, plaintext, |
| 8144 | 42 | | ciphertext, out var length); |
| | 43 | |
|
| 8144 | 44 | | if (result != 0) |
| | 45 | | { |
| 0 | 46 | | throw new CryptographicException("Encryption failed."); |
| | 47 | | } |
| | 48 | |
|
| | 49 | | Debug.Assert(length == plaintext.Length + CryptoConstants.CHACHA20_POLY1305_TAG_LEN); |
| 8144 | 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.PRIVKEY_LEN); |
| | 62 | | Debug.Assert(ciphertext.Length >= CryptoConstants.CHACHA20_POLY1305_TAG_LEN); |
| | 63 | | Debug.Assert(plaintext.Length >= ciphertext.Length - CryptoConstants.CHACHA20_POLY1305_TAG_LEN); |
| | 64 | |
|
| 8136 | 65 | | Span<byte> nonce = stackalloc byte[CryptoConstants.CHACHA20_POLY1305_NONCE_LEN]; |
| 8136 | 66 | | BinaryPrimitives.WriteUInt64LittleEndian(nonce[4..], publicNonce); |
| | 67 | |
|
| 8136 | 68 | | var result = _cryptoProvider.AeadChaCha20Poly1305IetfDecrypt(key, nonce, null, authenticationData, |
| 8136 | 69 | | ciphertext, plaintext, out var length); |
| | 70 | |
|
| 8128 | 71 | | if (result != 0) |
| | 72 | | { |
| 8 | 73 | | throw new CryptographicException("Decryption failed."); |
| | 74 | | } |
| | 75 | |
|
| | 76 | | Debug.Assert(length == ciphertext.Length - CryptoConstants.CHACHA20_POLY1305_TAG_LEN); |
| 8120 | 77 | | return (int)length; |
| | 78 | | } |
| | 79 | |
|
| | 80 | | public void Dispose() |
| | 81 | | { |
| 144 | 82 | | _cryptoProvider.Dispose(); |
| 144 | 83 | | } |
| | 84 | | } |