Skip to content

Commit

Permalink
update packages and implement SecureMemory (#34)
Browse files Browse the repository at this point in the history
Update packages and implement SecureMemory
  • Loading branch information
nGoline authored Jan 27, 2025
1 parent f7cfdf6 commit 3902f4c
Show file tree
Hide file tree
Showing 31 changed files with 507 additions and 197 deletions.
5 changes: 3 additions & 2 deletions samples/Bolt11.Decoder.Blazor/Bolt11.Decoder.Blazor.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,9 @@

<ItemGroup>
<PackageReference Include="BlazorDeferredRemove" Version="0.2.5" />
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly" Version="8.0.8"/>
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.DevServer" Version="8.0.8" PrivateAssets="all"/>
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly" Version="8.0.12" />
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.DevServer" Version="8.0.12" PrivateAssets="all" />
<PackageReference Include="Microsoft.NET.ILLink.Tasks" Version="8.0.12" />
<PackageReference Include="NLightning.Bolt11.Blazor" Version="0.2.2"/>
</ItemGroup>

Expand Down
4 changes: 2 additions & 2 deletions src/NLightning.Bolt11/NLightning.Bolt11.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -98,11 +98,11 @@
<ItemGroup Condition="'$(UsingMicrosoftNETSdkBlazorWebAssembly)' != 'true' And '$(PublishAot)' != 'true' And !$(Configuration.Contains('.Wasm')) And !$(Configuration.Contains('.Native'))">
<Compile Include="..\NLightning.Common\Crypto\Providers\Libsodium\*.cs" LinkBase="Common\Crypto\Providers\Libsodium"/>

<PackageReference Include="libsodium" Version="1.0.19"/>
<PackageReference Include="libsodium" Version="1.0.20.1" />
</ItemGroup>

<ItemGroup>
<PackageReference Include="NBitcoin" Version="7.0.37"/>
<PackageReference Include="NBitcoin" Version="7.0.50" />
</ItemGroup>

<ItemGroup>
Expand Down
10 changes: 9 additions & 1 deletion src/NLightning.Bolts/BOLT1/Services/PeerService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,15 @@ public async Task ConnectToPeerAsync(PeerAddress peerAddress)

// Create and Initialize the transport service
var transportService = transportServiceFactory.CreateTransportService(true, nodeOptions.KeyPair.PrivateKey.ToBytes(), peerAddress.PubKey.ToBytes(), tcpClient);
await transportService.InitializeAsync(nodeOptions.NetworkTimeout);

try
{
await transportService.InitializeAsync(nodeOptions.NetworkTimeout);
}
catch (Exception ex)
{
throw new ConnectionException($"Error connecting to peer {peerAddress.Host}:{peerAddress.Port}", ex);
}

var peer = new Peer(nodeOptions, messageServiceFactory.CreateMessageService(transportService), pingPongServiceFactory.CreatePingPongService(nodeOptions.NetworkTimeout), false);
peer.DisconnectEvent += (_, _) =>
Expand Down
6 changes: 3 additions & 3 deletions src/NLightning.Bolts/BOLT11/Invoice.cs
Original file line number Diff line number Diff line change
Expand Up @@ -356,7 +356,7 @@ public Invoice(ulong amountMilliSats, string description, uint256 paymentHash, u
Network = ConfigManager.Instance.Network;
HumanReadablePart = BuildHumanReadablePart();
Timestamp = DateTimeOffset.UtcNow.ToUnixTimeSeconds();
Signature = new CompactSignature(-1, new byte[64]);
Signature = new CompactSignature(0, new byte[64]);

// Set Required Fields
PaymentHash = paymentHash;
Expand All @@ -381,7 +381,7 @@ public Invoice(ulong amountMilliSats, uint256 descriptionHash, uint256 paymentHa
Network = ConfigManager.Instance.Network;
HumanReadablePart = BuildHumanReadablePart();
Timestamp = DateTimeOffset.UtcNow.ToUnixTimeSeconds();
Signature = new CompactSignature(-1, new byte[64]);
Signature = new CompactSignature(0, new byte[64]);

// Set Required Fields
PaymentHash = paymentHash;
Expand All @@ -405,7 +405,7 @@ internal Invoice(Network network, ulong? amountMilliSats = 0, long? timestamp =
Network = network;
HumanReadablePart = BuildHumanReadablePart();
Timestamp = timestamp ?? DateTimeOffset.UtcNow.ToUnixTimeSeconds();
Signature = new CompactSignature(-1, new byte[64]);
Signature = new CompactSignature(0, new byte[64]);
}

/// <summary>
Expand Down
31 changes: 20 additions & 11 deletions src/NLightning.Bolts/BOLT8/Primitives/Transport.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
using System.Diagnostics;
using NLightning.Bolts.Exceptions;

namespace NLightning.Bolts.BOLT8.Primitives;

Expand Down Expand Up @@ -57,7 +57,12 @@ public int ReadMessageLength(ReadOnlySpan<byte> lc)

// Decrypt the payload length from the message buffer
var l = new byte[2];
var lcLen = ReadMessagePart(lc, l); // TODO: Check lcLen == 2
// Bytes read should always be 2
if (ReadMessagePart(lc, l) != 2)
{
throw new ConnectionException("Message Length was invalid.");
}

if (BitConverter.IsLittleEndian)
{
Array.Reverse(l);
Expand Down Expand Up @@ -101,7 +106,10 @@ private int WriteMessagePart(ReadOnlySpan<byte> payload, Span<byte> messageBuffe
}

var cipher = _initiator ? _sendingKey : _receivingKey;
Debug.Assert(cipher.HasKey());
if (!cipher.HasKeys())
{
throw new InvalidOperationException("Cipher is missing keys.");
}

return cipher.Encrypt(payload, messageBuffer);
}
Expand All @@ -127,14 +135,12 @@ private int WriteMessagePart(ReadOnlySpan<byte> payload, Span<byte> messageBuffe
/// </exception>
private int ReadMessagePart(ReadOnlySpan<byte> message, Span<byte> payloadBuffer)
{
if (message.Length > ProtocolConstants.MAX_MESSAGE_LENGTH)
switch (message.Length)
{
throw new ArgumentException($"Noise message must be less than or equal to {ProtocolConstants.MAX_MESSAGE_LENGTH} bytes in length.");
}

if (message.Length < CryptoConstants.CHACHA20_POLY1305_TAG_LEN)
{
throw new ArgumentException($"Noise message must be greater than or equal to {CryptoConstants.CHACHA20_POLY1305_TAG_LEN} bytes in length.");
case > ProtocolConstants.MAX_MESSAGE_LENGTH:
throw new ArgumentException($"Noise message must be less than or equal to {ProtocolConstants.MAX_MESSAGE_LENGTH} bytes in length.");
case < CryptoConstants.CHACHA20_POLY1305_TAG_LEN:
throw new ArgumentException($"Noise message must be greater than or equal to {CryptoConstants.CHACHA20_POLY1305_TAG_LEN} bytes in length.");
}

if (message.Length - CryptoConstants.CHACHA20_POLY1305_TAG_LEN > payloadBuffer.Length)
Expand All @@ -143,7 +149,10 @@ private int ReadMessagePart(ReadOnlySpan<byte> message, Span<byte> payloadBuffer
}

var cipher = _initiator ? _receivingKey : _sendingKey;
Debug.Assert(cipher.HasKey());
if (!cipher.HasKeys())
{
throw new InvalidOperationException("Cipher is missing keys.");
}

return cipher.Decrypt(message, payloadBuffer);
}
Expand Down
27 changes: 8 additions & 19 deletions src/NLightning.Bolts/BOLT8/Services/HandshakeService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -32,26 +32,17 @@ public int PerformStep(ReadOnlySpan<byte> inMessage, Span<byte> outMessage)
if (_steps == 2)
{
_steps--;
if (IsInitiator)
{
return InitiatorWriteActOne(outMessage);
}
else
{
return ResponderReadActOneAndWriteActTwo(inMessage, outMessage);
}
return IsInitiator
? InitiatorWriteActOne(outMessage)
: ResponderReadActOneAndWriteActTwo(inMessage, outMessage);
}
else if (_steps == 1)

if (_steps == 1)
{
_steps--;
if (IsInitiator)
{
return InitiatorReadActTwoAndWriteActThree(inMessage, outMessage);
}
else
{
return ResponderReadActThree(inMessage);
}
return IsInitiator
? InitiatorReadActTwoAndWriteActThree(inMessage, outMessage)
: ResponderReadActThree(inMessage);
}

throw new InvalidOperationException("There's no more steps to complete");
Expand Down Expand Up @@ -125,7 +116,5 @@ private int ResponderReadActThree(ReadOnlySpan<byte> actThreeMessage)
public void Dispose()
{
_handshakeState.Dispose();
handshakeState?.Dispose();
Transport?.Dispose();
}
}
58 changes: 26 additions & 32 deletions src/NLightning.Bolts/BOLT8/States/CipherState.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ namespace NLightning.Bolts.BOLT8.States;
using Common.Constants;
using Common.Crypto.Ciphers;
using Common.Crypto.Functions;
using Common.Crypto.Primitives;

/// <summary>
/// A CipherState can encrypt and decrypt data based on its variables k
Expand All @@ -16,23 +17,10 @@ internal sealed class CipherState : IDisposable

private readonly ChaCha20Poly1305 _cipher = new();
private readonly Hkdf _hkdf = new();
private byte[]? _ck;
private byte[]? _k;
private SecureMemory? _ck;
private SecureMemory? _k;
private ulong _n;

/// <summary>
/// Sets k = key. Sets n = 0.
/// </summary>
public void InitializeKey(ReadOnlySpan<byte> key)
{
Debug.Assert(key.Length == CryptoConstants.PRIVKEY_LEN);

_k ??= new byte[CryptoConstants.PRIVKEY_LEN];
key.CopyTo(_k);

_n = 0;
}

/// <summary>
/// Sets _k = key. Sets _ck = ck. Sets _n = 0.
/// </summary>
Expand All @@ -41,21 +29,21 @@ public void InitializeKeyAndChainingKey(ReadOnlySpan<byte> key, ReadOnlySpan<byt
Debug.Assert(key.Length == CryptoConstants.PRIVKEY_LEN);
Debug.Assert(chainingKey.Length == CryptoConstants.PRIVKEY_LEN);

_k ??= new byte[CryptoConstants.PRIVKEY_LEN];
_k ??= new SecureMemory(CryptoConstants.PRIVKEY_LEN);
key.CopyTo(_k);

_ck ??= new byte[CryptoConstants.PRIVKEY_LEN];
_ck ??= new SecureMemory(CryptoConstants.PRIVKEY_LEN);
chainingKey.CopyTo(_ck);

_n = 0;
}

/// <summary>
/// Returns true if k is non-empty, false otherwise.
/// Returns true if k and ck are non-empty, false otherwise.
/// </summary>
public bool HasKey()
public bool HasKeys()
{
return _k != null;
return _k is not null && _ck is not null;
}

/// <summary>
Expand All @@ -68,7 +56,7 @@ public void SetNonce(ulong nonce)

/// <summary>
/// If k is non-empty returns ENCRYPT(k, n++, ad, plaintext).
/// Otherwise copies the plaintext to the ciphertext parameter
/// Otherwise, copies the plaintext to the ciphertext parameter
/// and returns the length of the plaintext.
/// </summary>
public int EncryptWithAd(ReadOnlySpan<byte> ad, ReadOnlySpan<byte> plaintext, Span<byte> ciphertext)
Expand All @@ -78,18 +66,18 @@ public int EncryptWithAd(ReadOnlySpan<byte> ad, ReadOnlySpan<byte> plaintext, Sp
throw new OverflowException("Nonce has reached its maximum value.");
}

if (_k == null)
if (_k != null)
{
plaintext.CopyTo(ciphertext);
return plaintext.Length;
return _cipher.Encrypt(_k, _n++, ad, plaintext, ciphertext);
}

return _cipher.Encrypt(_k, _n++, ad, plaintext, ciphertext);
plaintext.CopyTo(ciphertext);
return plaintext.Length;
}

/// <summary>
/// If k is non-empty returns DECRYPT(k, n++, ad, ciphertext).
/// Otherwise copies the ciphertext to the plaintext parameter and returns
/// Otherwise, copies the ciphertext to the plaintext parameter and returns
/// the length of the ciphertext. If an authentication failure occurs
/// then n is not incremented and an error is signaled to the caller.
/// </summary>
Expand Down Expand Up @@ -126,7 +114,7 @@ public int Encrypt(ReadOnlySpan<byte> plaintext, Span<byte> ciphertext)
Rekey();
}

return _cipher.Encrypt(_k, _n++, null, plaintext, ciphertext);
return _cipher.Encrypt(_k!, _n++, null, plaintext, ciphertext);
}

/// <summary>
Expand All @@ -141,30 +129,36 @@ public int Decrypt(ReadOnlySpan<byte> ciphertext, Span<byte> plaintext)
{
Rekey();
}
return _cipher.Decrypt(_k, _n++, null, ciphertext, plaintext);
return _cipher.Decrypt(_k!, _n++, null, ciphertext, plaintext);
}

/// <summary>
/// Sets k = REKEY(k).
/// </summary>
public void Rekey()
{
Debug.Assert(HasKey());
if (!HasKeys())
{
throw new NullReferenceException("Keys are missing");
}

_n = 0;

Span<byte> key = stackalloc byte[CryptoConstants.PRIVKEY_LEN * 2];
_hkdf.ExtractAndExpand2(_ck, _k, key);
_hkdf.ExtractAndExpand2(_ck!, _k!, key);

_ck ??= new byte[CryptoConstants.PRIVKEY_LEN];
_ck ??= new SecureMemory(CryptoConstants.PRIVKEY_LEN);
key[..CryptoConstants.PRIVKEY_LEN].CopyTo(_ck);

_k ??= new byte[CryptoConstants.PRIVKEY_LEN];
_k ??= new SecureMemory(CryptoConstants.PRIVKEY_LEN);
key[CryptoConstants.PRIVKEY_LEN..].CopyTo(_k);
}

public void Dispose()
{
_ck?.Dispose();
_k?.Dispose();
_hkdf.Dispose();
_cipher.Dispose();
}
}
13 changes: 6 additions & 7 deletions src/NLightning.Bolts/BOLT8/States/HandshakeState.cs
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ public HandshakeState(bool initiator, ReadOnlySpan<byte> s, ReadOnlySpan<byte> r
throw new InvalidOperationException("Cannot call WriteMessage after the handshake has already been completed.");
}

var overhead = _messagePatterns.Peek().Overhead(CryptoConstants.PUBKEY_LEN, _state.HasKey());
var overhead = _messagePatterns.Peek().Overhead(CryptoConstants.PUBKEY_LEN, _state.HasKeys());
var ciphertextSize = payload.Length + overhead;

if (ciphertextSize > ProtocolConstants.MAX_MESSAGE_LENGTH)
Expand Down Expand Up @@ -155,7 +155,7 @@ public HandshakeState(bool initiator, ReadOnlySpan<byte> s, ReadOnlySpan<byte> r
throw new InvalidOperationException("Cannot call WriteMessage after the handshake has already been completed.");
}

var overhead = _messagePatterns.Peek().Overhead(CryptoConstants.PUBKEY_LEN, _state.HasKey());
var overhead = _messagePatterns.Peek().Overhead(CryptoConstants.PUBKEY_LEN, _state.HasKeys());
var plaintextSize = message.Length - overhead;

if (message.Length > ProtocolConstants.MAX_MESSAGE_LENGTH)
Expand Down Expand Up @@ -285,7 +285,7 @@ private ReadOnlySpan<byte> ReadS(ReadOnlySpan<byte> message)
}
message = message[1..];

var length = _state.HasKey() ? CryptoConstants.PUBKEY_LEN + CryptoConstants.CHACHA20_POLY1305_TAG_LEN : CryptoConstants.PUBKEY_LEN;
var length = _state.HasKeys() ? CryptoConstants.PUBKEY_LEN + CryptoConstants.CHACHA20_POLY1305_TAG_LEN : CryptoConstants.PUBKEY_LEN;
var temp = message[..length];

_rs = new byte[CryptoConstants.PUBKEY_LEN];
Expand Down Expand Up @@ -343,8 +343,8 @@ private void DhAndMixKey(KeyPair? keyPair, ReadOnlySpan<byte> publicKey)
private void Clear()
{
_state.Dispose();
_s.Dispose();
_e?.Dispose();
_s.Dispose();
}

private enum Role
Expand All @@ -355,8 +355,7 @@ private enum Role

public void Dispose()
{
_state.Dispose();
_s.Dispose();
_e?.Dispose();
Clear();
GC.SuppressFinalize(this);
}
}
Loading

0 comments on commit 3902f4c

Please sign in to comment.