< Summary - Combined Code Coverage

Information
Class: NLightning.Infrastructure.Transport.Handshake.States.SymmetricState
Assembly: NLightning.Infrastructure
File(s): /home/runner/work/nlightning/nlightning/src/NLightning.Infrastructure/Transport/Handshake/States/SymmetricState.cs
Tag: 30_15166811759
Line coverage
80%
Covered lines: 57
Uncovered lines: 14
Coverable lines: 71
Total lines: 199
Line coverage: 80.2%
Branch coverage
50%
Covered branches: 6
Total branches: 12
Branch coverage: 50%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
.ctor(...)50%2292.31%
MixKey(...)75%4.02490%
MixHash(...)100%11100%
MixKeyAndHash(...)0%2040%
GetHandshakeHash()100%11100%
EncryptAndHash(...)100%11100%
DecryptAndHash(...)100%11100%
Split()100%11100%
HasKeys()100%11100%
Dispose()100%22100%

File(s)

/home/runner/work/nlightning/nlightning/src/NLightning.Infrastructure/Transport/Handshake/States/SymmetricState.cs

#LineLine coverage
 1namespace NLightning.Infrastructure.Transport.Handshake.States;
 2
 3using Common.Utils;
 4using Crypto.Factories;
 5using Crypto.Functions;
 6using Crypto.Hashes;
 7using Crypto.Interfaces;
 8using Crypto.Primitives;
 9using Domain.Crypto.Constants;
 10
 11/// <summary>
 12/// A SymmetricState object contains a CipherState plus ck (a chaining
 13/// key of HashLen bytes) and h (a hash output of HashLen bytes).
 14/// </summary>
 15internal sealed class SymmetricState : IDisposable
 16{
 17    private readonly ICryptoProvider _cryptoProvider;
 9618    private readonly Sha256 _sha256 = new();
 9619    private readonly Hkdf _hkdf = new();
 9620    private readonly CipherState _state = new();
 21    private readonly SecureMemory _ck;
 22    private readonly byte[] _h;
 23
 24    private bool _disposed;
 25
 26    /// <summary>
 27    /// Initializes a new SymmetricState with an
 28    /// arbitrary-length protocolName byte sequence.
 29    /// </summary>
 9630    public SymmetricState(ReadOnlySpan<byte> protocolName)
 31    {
 9632        _cryptoProvider = CryptoFactory.GetCryptoProvider();
 9633        _ck = new SecureMemory(CryptoConstants.SHA256_HASH_LEN);
 9634        _h = new byte[CryptoConstants.SHA256_HASH_LEN];
 35
 9636        if (protocolName.Length <= CryptoConstants.SHA256_HASH_LEN)
 37        {
 038            protocolName.CopyTo(_h);
 39        }
 40        else
 41        {
 9642            _sha256.AppendData(protocolName);
 9643            _sha256.GetHashAndReset(_h);
 44        }
 45
 9646        _h.CopyTo(_ck);
 9647    }
 48
 49    /// <summary>
 50    /// Sets ck, tempK = HKDF(ck, inputKeyMaterial, 2).
 51    /// If HashLen is 64, then truncates tempK to 32 bytes.
 52    /// Calls InitializeKey(tempK).
 53    /// </summary>
 54    public void MixKey(ReadOnlySpan<byte> inputKeyMaterial)
 55    {
 18056        ExceptionUtils.ThrowIfDisposed(_disposed, nameof(Hkdf));
 57
 18058        var length = inputKeyMaterial.Length;
 18059        if (length != 0 && length != CryptoConstants.PRIVKEY_LEN)
 60        {
 061            throw new ArgumentOutOfRangeException(nameof(inputKeyMaterial), $"Length should be either 0 or {CryptoConsta
 62        }
 63
 18064        Span<byte> output = stackalloc byte[2 * CryptoConstants.SHA256_HASH_LEN];
 18065        _hkdf.ExtractAndExpand2(_ck, inputKeyMaterial, output);
 66
 18067        output[..CryptoConstants.SHA256_HASH_LEN].CopyTo(_ck);
 68
 18069        var tempK = output.Slice(CryptoConstants.SHA256_HASH_LEN, CryptoConstants.PRIVKEY_LEN);
 18070        _state.InitializeKeyAndChainingKey(tempK, _ck);
 18071    }
 72
 73    /// <summary>
 74    /// Sets h = HASH(h || data).
 75    /// </summary>
 76    public void MixHash(ReadOnlySpan<byte> data)
 77    {
 55278        ExceptionUtils.ThrowIfDisposed(_disposed, nameof(Hkdf));
 79
 55280        _sha256.AppendData(_h);
 55281        _sha256.AppendData(data);
 55282        _sha256.GetHashAndReset(_h);
 55283    }
 84
 85    /// <summary>
 86    /// Sets ck, tempH, tempK = HKDF(ck, inputKeyMaterial, 3).
 87    /// Calls MixHash(tempH).
 88    /// If HashLen is 64, then truncates tempK to 32 bytes.
 89    /// Calls InitializeKey(tempK).
 90    /// </summary>
 91    public void MixKeyAndHash(ReadOnlySpan<byte> inputKeyMaterial)
 92    {
 093        ExceptionUtils.ThrowIfDisposed(_disposed, nameof(Hkdf));
 94
 095        var length = inputKeyMaterial.Length;
 096        if (length != 0 && length != CryptoConstants.PRIVKEY_LEN)
 97        {
 098            throw new ArgumentOutOfRangeException(nameof(inputKeyMaterial), $"Length should be either 0 or {CryptoConsta
 99        }
 100
 0101        Span<byte> output = stackalloc byte[3 * CryptoConstants.SHA256_HASH_LEN];
 0102        _hkdf.ExtractAndExpand3(_ck, inputKeyMaterial, output);
 103
 0104        output[..CryptoConstants.SHA256_HASH_LEN].CopyTo(_ck);
 105
 0106        var tempH = output.Slice(CryptoConstants.SHA256_HASH_LEN, CryptoConstants.SHA256_HASH_LEN);
 0107        var tempK = output.Slice(2 * CryptoConstants.SHA256_HASH_LEN, CryptoConstants.PRIVKEY_LEN);
 108
 0109        MixHash(tempH);
 0110        _state.InitializeKeyAndChainingKey(tempK, _ck);
 0111    }
 112
 113    /// <summary>
 114    /// Returns h. This function should only be called at the end of
 115    /// a handshake, i.e. after the Split() function has been called.
 116    /// </summary>
 117    public byte[] GetHandshakeHash()
 118    {
 32119        ExceptionUtils.ThrowIfDisposed(_disposed, nameof(Hkdf));
 120
 32121        return _h;
 122    }
 123
 124    /// <summary>
 125    /// Sets ciphertext = EncryptWithAd(h, plaintext),
 126    /// calls MixHash(ciphertext), and returns ciphertext.
 127    /// </summary>
 128    public int EncryptAndHash(ReadOnlySpan<byte> plaintext, Span<byte> ciphertext)
 129    {
 108130        ExceptionUtils.ThrowIfDisposed(_disposed, nameof(Hkdf));
 131
 108132        var bytesWritten = _state.EncryptWithAd(_h, plaintext, ciphertext);
 108133        MixHash(ciphertext[..bytesWritten]);
 134
 108135        return bytesWritten;
 136    }
 137
 138    /// <summary>
 139    /// Sets plaintext = DecryptWithAd(h, ciphertext),
 140    /// calls MixHash(ciphertext), and returns plaintext.
 141    /// </summary>
 142    public int DecryptAndHash(ReadOnlySpan<byte> ciphertext, Span<byte> plaintext)
 143    {
 116144        ExceptionUtils.ThrowIfDisposed(_disposed, nameof(Hkdf));
 145
 116146        var bytesRead = _state.DecryptWithAd(_h, ciphertext, plaintext);
 100147        MixHash(ciphertext);
 148
 100149        return bytesRead;
 150    }
 151
 152    /// <summary>
 153    /// Returns a pair of CipherState objects for encrypting transport messages.
 154    /// </summary>
 155    public (CipherState c1, CipherState c2) Split()
 156    {
 32157        ExceptionUtils.ThrowIfDisposed(_disposed, nameof(Hkdf));
 158
 32159        Span<byte> output = stackalloc byte[2 * CryptoConstants.SHA256_HASH_LEN];
 32160        _hkdf.ExtractAndExpand2(_ck, null, output);
 161
 32162        var tempK1 = output[..CryptoConstants.PRIVKEY_LEN];
 32163        var tempK2 = output.Slice(CryptoConstants.SHA256_HASH_LEN, CryptoConstants.PRIVKEY_LEN);
 164
 32165        var c1 = new CipherState();
 32166        var c2 = new CipherState();
 167
 32168        c1.InitializeKeyAndChainingKey(tempK1, _ck);
 32169        c2.InitializeKeyAndChainingKey(tempK2, _ck);
 170
 32171        return (c1, c2);
 172    }
 173
 174    /// <summary>
 175    /// Returns true if k and ck are non-empty, false otherwise.
 176    /// </summary>
 177    public bool HasKeys()
 178    {
 244179        ExceptionUtils.ThrowIfDisposed(_disposed, nameof(Hkdf));
 180
 244181        return _state.HasKeys();
 182    }
 183
 184    public void Dispose()
 185    {
 128186        if (_disposed)
 187        {
 32188            return;
 189        }
 190
 96191        _ck.Dispose();
 96192        _state.Dispose();
 96193        _hkdf.Dispose();
 96194        _sha256.Dispose();
 96195        _cryptoProvider.Dispose();
 196
 96197        _disposed = true;
 96198    }
 199}