< 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: 36_15743069263
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 Crypto.Factories;
 4using Crypto.Functions;
 5using Crypto.Hashes;
 6using Crypto.Interfaces;
 7using Crypto.Primitives;
 8using Domain.Crypto.Constants;
 9using Domain.Utils;
 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;
 10418    private readonly Sha256 _sha256 = new();
 10419    private readonly Hkdf _hkdf = new();
 10420    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>
 10430    public SymmetricState(ReadOnlySpan<byte> protocolName)
 31    {
 10432        _cryptoProvider = CryptoFactory.GetCryptoProvider();
 10433        _ck = new SecureMemory(CryptoConstants.Sha256HashLen);
 10434        _h = new byte[CryptoConstants.Sha256HashLen];
 35
 10436        if (protocolName.Length <= CryptoConstants.Sha256HashLen)
 37        {
 038            protocolName.CopyTo(_h);
 39        }
 40        else
 41        {
 10442            _sha256.AppendData(protocolName);
 10443            _sha256.GetHashAndReset(_h);
 44        }
 45
 10446        _h.CopyTo(_ck);
 10447    }
 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    {
 18856        ExceptionUtils.ThrowIfDisposed(_disposed, nameof(Hkdf));
 57
 18858        var length = inputKeyMaterial.Length;
 18859        if (length != 0 && length != CryptoConstants.PrivkeyLen)
 60        {
 061            throw new ArgumentOutOfRangeException(nameof(inputKeyMaterial), $"Length should be either 0 or {CryptoConsta
 62        }
 63
 18864        Span<byte> output = stackalloc byte[2 * CryptoConstants.Sha256HashLen];
 18865        _hkdf.ExtractAndExpand2(_ck, inputKeyMaterial, output);
 66
 18867        output[..CryptoConstants.Sha256HashLen].CopyTo(_ck);
 68
 18869        var tempK = output.Slice(CryptoConstants.Sha256HashLen, CryptoConstants.PrivkeyLen);
 18870        _state.InitializeKeyAndChainingKey(tempK, _ck);
 18871    }
 72
 73    /// <summary>
 74    /// Sets h = HASH(h || data).
 75    /// </summary>
 76    public void MixHash(ReadOnlySpan<byte> data)
 77    {
 58078        ExceptionUtils.ThrowIfDisposed(_disposed, nameof(Hkdf));
 79
 58080        _sha256.AppendData(_h);
 58081        _sha256.AppendData(data);
 58082        _sha256.GetHashAndReset(_h);
 58083    }
 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.PrivkeyLen)
 97        {
 098            throw new ArgumentOutOfRangeException(nameof(inputKeyMaterial), $"Length should be either 0 or {CryptoConsta
 99        }
 100
 0101        Span<byte> output = stackalloc byte[3 * CryptoConstants.Sha256HashLen];
 0102        _hkdf.ExtractAndExpand3(_ck, inputKeyMaterial, output);
 103
 0104        output[..CryptoConstants.Sha256HashLen].CopyTo(_ck);
 105
 0106        var tempH = output.Slice(CryptoConstants.Sha256HashLen, CryptoConstants.Sha256HashLen);
 0107        var tempK = output.Slice(2 * CryptoConstants.Sha256HashLen, CryptoConstants.PrivkeyLen);
 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    {
 112130        ExceptionUtils.ThrowIfDisposed(_disposed, nameof(Hkdf));
 131
 112132        var bytesWritten = _state.EncryptWithAd(_h, plaintext, ciphertext);
 112133        MixHash(ciphertext[..bytesWritten]);
 134
 112135        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    {
 120144        ExceptionUtils.ThrowIfDisposed(_disposed, nameof(Hkdf));
 145
 120146        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.Sha256HashLen];
 32160        _hkdf.ExtractAndExpand2(_ck, null, output);
 161
 32162        var tempK1 = output[..CryptoConstants.PrivkeyLen];
 32163        var tempK2 = output.Slice(CryptoConstants.Sha256HashLen, CryptoConstants.PrivkeyLen);
 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    {
 252179        ExceptionUtils.ThrowIfDisposed(_disposed, nameof(Hkdf));
 180
 252181        return _state.HasKeys();
 182    }
 183
 184    public void Dispose()
 185    {
 136186        if (_disposed)
 187        {
 32188            return;
 189        }
 190
 104191        _ck.Dispose();
 104192        _state.Dispose();
 104193        _hkdf.Dispose();
 104194        _sha256.Dispose();
 104195        _cryptoProvider.Dispose();
 196
 104197        _disposed = true;
 104198    }
 199}