diff --git a/NLightning.sln b/NLightning.sln index 380d2e0..ca50bc9 100644 --- a/NLightning.sln +++ b/NLightning.sln @@ -33,11 +33,22 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "BlazorTests", "BlazorTests" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NLightning.Blazor.Tests", "test\BlazorTests\NLightning.Blazor.Tests\NLightning.Blazor.Tests.csproj", "{C0F21413-47D0-4584-84A5-B730909E7349}" EndProject -EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "samples", "samples", "{29E3DE2A-9C25-4F48-8FCB-0BBAA9699662}" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Bolt11.Decoder.Blazor", "samples\Bolt11.Decoder.Blazor\Bolt11.Decoder.Blazor.csproj", "{D80680EA-8B09-44BA-A9EE-435C93D298B1}" EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Fuzztests", "Fuzztests", "{965A5904-F5EC-40EE-822C-3E6912640F80}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "NLightning.Bolts.BOLT11", "NLightning.Bolts.BOLT11", "{79B6F41F-3E5A-4ABF-83A4-5860F9ADAC35}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "InvoiceRoundTripFuzzer", "test\FuzzTests\NLightning.Bolts.BOLT11\InvoiceRoundTripFuzzer\InvoiceRoundTripFuzzer.csproj", "{201CF0D3-AA8A-40D3-843E-4382F9EDEABA}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "NLightning.Bolts.BOLT8", "NLightning.Bolts.BOLT8", "{6BECD5C6-9978-41E1-BAC0-854E16F0D142}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ActOneFuzzer", "test\FuzzTests\NLightning.Bolts.BOLT8\ActOneFuzzer\ActOneFuzzer.csproj", "{A7C2075F-1C1E-4EE6-A2FE-D65EFBB2D662}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ActTwoFuzzer", "test\FuzzTests\NLightning.Bolts.BOLT8\ActTwoFuzzer\ActTwoFuzzer.csproj", "{8A99EB76-83DA-4E88-A7F8-265224F901D5}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Release|Any CPU = Release|Any CPU @@ -176,6 +187,42 @@ Global {D80680EA-8B09-44BA-A9EE-435C93D298B1}.Debug|Any CPU.Build.0 = Debug|Any CPU {D80680EA-8B09-44BA-A9EE-435C93D298B1}.Release|Any CPU.ActiveCfg = Release|Any CPU {D80680EA-8B09-44BA-A9EE-435C93D298B1}.Release|Any CPU.Build.0 = Release|Any CPU + {201CF0D3-AA8A-40D3-843E-4382F9EDEABA}.Release|Any CPU.ActiveCfg = Release|Any CPU + {201CF0D3-AA8A-40D3-843E-4382F9EDEABA}.Release|Any CPU.Build.0 = Release|Any CPU + {201CF0D3-AA8A-40D3-843E-4382F9EDEABA}.Release.Native|Any CPU.ActiveCfg = Debug|Any CPU + {201CF0D3-AA8A-40D3-843E-4382F9EDEABA}.Release.Native|Any CPU.Build.0 = Debug|Any CPU + {201CF0D3-AA8A-40D3-843E-4382F9EDEABA}.Release.Wasm|Any CPU.ActiveCfg = Debug|Any CPU + {201CF0D3-AA8A-40D3-843E-4382F9EDEABA}.Release.Wasm|Any CPU.Build.0 = Debug|Any CPU + {201CF0D3-AA8A-40D3-843E-4382F9EDEABA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {201CF0D3-AA8A-40D3-843E-4382F9EDEABA}.Debug|Any CPU.Build.0 = Debug|Any CPU + {201CF0D3-AA8A-40D3-843E-4382F9EDEABA}.Debug.Native|Any CPU.ActiveCfg = Debug|Any CPU + {201CF0D3-AA8A-40D3-843E-4382F9EDEABA}.Debug.Native|Any CPU.Build.0 = Debug|Any CPU + {201CF0D3-AA8A-40D3-843E-4382F9EDEABA}.Debug.Wasm|Any CPU.ActiveCfg = Debug|Any CPU + {201CF0D3-AA8A-40D3-843E-4382F9EDEABA}.Debug.Wasm|Any CPU.Build.0 = Debug|Any CPU + {A7C2075F-1C1E-4EE6-A2FE-D65EFBB2D662}.Release|Any CPU.ActiveCfg = Release|Any CPU + {A7C2075F-1C1E-4EE6-A2FE-D65EFBB2D662}.Release|Any CPU.Build.0 = Release|Any CPU + {A7C2075F-1C1E-4EE6-A2FE-D65EFBB2D662}.Release.Native|Any CPU.ActiveCfg = Debug|Any CPU + {A7C2075F-1C1E-4EE6-A2FE-D65EFBB2D662}.Release.Native|Any CPU.Build.0 = Debug|Any CPU + {A7C2075F-1C1E-4EE6-A2FE-D65EFBB2D662}.Release.Wasm|Any CPU.ActiveCfg = Debug|Any CPU + {A7C2075F-1C1E-4EE6-A2FE-D65EFBB2D662}.Release.Wasm|Any CPU.Build.0 = Debug|Any CPU + {A7C2075F-1C1E-4EE6-A2FE-D65EFBB2D662}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {A7C2075F-1C1E-4EE6-A2FE-D65EFBB2D662}.Debug|Any CPU.Build.0 = Debug|Any CPU + {A7C2075F-1C1E-4EE6-A2FE-D65EFBB2D662}.Debug.Native|Any CPU.ActiveCfg = Debug|Any CPU + {A7C2075F-1C1E-4EE6-A2FE-D65EFBB2D662}.Debug.Native|Any CPU.Build.0 = Debug|Any CPU + {A7C2075F-1C1E-4EE6-A2FE-D65EFBB2D662}.Debug.Wasm|Any CPU.ActiveCfg = Debug|Any CPU + {A7C2075F-1C1E-4EE6-A2FE-D65EFBB2D662}.Debug.Wasm|Any CPU.Build.0 = Debug|Any CPU + {8A99EB76-83DA-4E88-A7F8-265224F901D5}.Release|Any CPU.ActiveCfg = Release|Any CPU + {8A99EB76-83DA-4E88-A7F8-265224F901D5}.Release|Any CPU.Build.0 = Release|Any CPU + {8A99EB76-83DA-4E88-A7F8-265224F901D5}.Release.Native|Any CPU.ActiveCfg = Debug|Any CPU + {8A99EB76-83DA-4E88-A7F8-265224F901D5}.Release.Native|Any CPU.Build.0 = Debug|Any CPU + {8A99EB76-83DA-4E88-A7F8-265224F901D5}.Release.Wasm|Any CPU.ActiveCfg = Debug|Any CPU + {8A99EB76-83DA-4E88-A7F8-265224F901D5}.Release.Wasm|Any CPU.Build.0 = Debug|Any CPU + {8A99EB76-83DA-4E88-A7F8-265224F901D5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {8A99EB76-83DA-4E88-A7F8-265224F901D5}.Debug|Any CPU.Build.0 = Debug|Any CPU + {8A99EB76-83DA-4E88-A7F8-265224F901D5}.Debug.Native|Any CPU.ActiveCfg = Debug|Any CPU + {8A99EB76-83DA-4E88-A7F8-265224F901D5}.Debug.Native|Any CPU.Build.0 = Debug|Any CPU + {8A99EB76-83DA-4E88-A7F8-265224F901D5}.Debug.Wasm|Any CPU.ActiveCfg = Debug|Any CPU + {8A99EB76-83DA-4E88-A7F8-265224F901D5}.Debug.Wasm|Any CPU.Build.0 = Debug|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -198,5 +245,11 @@ Global {99E240CC-6381-40DF-B784-528D2EDBBDFC} = {6F76E9AF-7E6D-42D8-918A-4C5B26BE07C1} {C0F21413-47D0-4584-84A5-B730909E7349} = {6F76E9AF-7E6D-42D8-918A-4C5B26BE07C1} {D80680EA-8B09-44BA-A9EE-435C93D298B1} = {29E3DE2A-9C25-4F48-8FCB-0BBAA9699662} + {965A5904-F5EC-40EE-822C-3E6912640F80} = {AF4411D4-8EE9-423E-8213-1C9D35E47882} + {79B6F41F-3E5A-4ABF-83A4-5860F9ADAC35} = {965A5904-F5EC-40EE-822C-3E6912640F80} + {201CF0D3-AA8A-40D3-843E-4382F9EDEABA} = {79B6F41F-3E5A-4ABF-83A4-5860F9ADAC35} + {6BECD5C6-9978-41E1-BAC0-854E16F0D142} = {965A5904-F5EC-40EE-822C-3E6912640F80} + {A7C2075F-1C1E-4EE6-A2FE-D65EFBB2D662} = {6BECD5C6-9978-41E1-BAC0-854E16F0D142} + {8A99EB76-83DA-4E88-A7F8-265224F901D5} = {6BECD5C6-9978-41E1-BAC0-854E16F0D142} EndGlobalSection EndGlobal diff --git a/src/NLightning.Bolts/AssemblyInfo.cs b/src/NLightning.Bolts/AssemblyInfo.cs index ad60fd6..d9bfb36 100644 --- a/src/NLightning.Bolts/AssemblyInfo.cs +++ b/src/NLightning.Bolts/AssemblyInfo.cs @@ -1,4 +1,6 @@ using System.Runtime.CompilerServices; [assembly: InternalsVisibleTo("NLightning.Bolts.Tests")] -[assembly: InternalsVisibleTo("DynamicProxyGenAssembly2")] \ No newline at end of file +[assembly: InternalsVisibleTo("DynamicProxyGenAssembly2")] +[assembly: InternalsVisibleTo("ActOneFuzzer")] +[assembly: InternalsVisibleTo("ActTwoFuzzer")] \ No newline at end of file diff --git a/test/FuzzTests/NLightning.Bolts.BOLT11/InvoiceRoundTripFuzzer/InvoiceRoundTripFuzzer.csproj b/test/FuzzTests/NLightning.Bolts.BOLT11/InvoiceRoundTripFuzzer/InvoiceRoundTripFuzzer.csproj new file mode 100644 index 0000000..d66d099 --- /dev/null +++ b/test/FuzzTests/NLightning.Bolts.BOLT11/InvoiceRoundTripFuzzer/InvoiceRoundTripFuzzer.csproj @@ -0,0 +1,18 @@ + + + + Exe + net8.0 + enable + enable + + + + + + + + + + + diff --git a/test/FuzzTests/NLightning.Bolts.BOLT11/InvoiceRoundTripFuzzer/Program.cs b/test/FuzzTests/NLightning.Bolts.BOLT11/InvoiceRoundTripFuzzer/Program.cs new file mode 100644 index 0000000..93d0534 --- /dev/null +++ b/test/FuzzTests/NLightning.Bolts.BOLT11/InvoiceRoundTripFuzzer/Program.cs @@ -0,0 +1,33 @@ +using NLightning.Bolts.BOLT11; +using NLightning.Bolts.Exceptions; +using SharpFuzz; + +namespace InvoiceRoundTripFuzzer; + +internal abstract class InvoiceRoundTripFuzzer +{ + private static void Main() + { + Fuzzer.OutOfProcess.Run(stream => + { + //pwsh ../../fuzz.ps1 InvoiceRoundTripFuzzer.csproj -i Testcases + try + { + using var reader = new StreamReader(stream); + + var invoiceData = reader.ReadToEnd(); + + var invoice = Invoice.Decode(invoiceData); + + var encodedInvoice = invoice.Encode(); + + if (!invoiceData.Equals(encodedInvoice, StringComparison.OrdinalIgnoreCase)) + { + throw new Exception("Invoice mismatch"); + } + + } + catch (InvoiceSerializationException) { } + }); + } +} \ No newline at end of file diff --git a/test/FuzzTests/NLightning.Bolts.BOLT11/InvoiceRoundTripFuzzer/Testcases/sample1.txt b/test/FuzzTests/NLightning.Bolts.BOLT11/InvoiceRoundTripFuzzer/Testcases/sample1.txt new file mode 100644 index 0000000..6e4c257 --- /dev/null +++ b/test/FuzzTests/NLightning.Bolts.BOLT11/InvoiceRoundTripFuzzer/Testcases/sample1.txt @@ -0,0 +1 @@ +lnbc2500n1p0j5lkpp5ujnc3jk8zzxy8ycqzrfpp5f8n5x2c7csthrunm8lzw29tuk3l8qprn5h63qsp5u3u7t \ No newline at end of file diff --git a/test/FuzzTests/NLightning.Bolts.BOLT8/ActOneFuzzer/ActOneFuzzer.csproj b/test/FuzzTests/NLightning.Bolts.BOLT8/ActOneFuzzer/ActOneFuzzer.csproj new file mode 100644 index 0000000..e08d49a --- /dev/null +++ b/test/FuzzTests/NLightning.Bolts.BOLT8/ActOneFuzzer/ActOneFuzzer.csproj @@ -0,0 +1,19 @@ + + + + Exe + net8.0 + enable + enable + + + + + + + + + + + + diff --git a/test/FuzzTests/NLightning.Bolts.BOLT8/ActOneFuzzer/Program.cs b/test/FuzzTests/NLightning.Bolts.BOLT8/ActOneFuzzer/Program.cs new file mode 100644 index 0000000..9873ec5 --- /dev/null +++ b/test/FuzzTests/NLightning.Bolts.BOLT8/ActOneFuzzer/Program.cs @@ -0,0 +1,40 @@ +using System.Security.Cryptography; +using NLightning.Bolts.BOLT8.Constants; +using NLightning.Bolts.BOLT8.States; +using NLightning.Bolts.Tests.BOLT8.Mock; +using NLightning.Bolts.Tests.BOLT8.Utils; +using SharpFuzz; + +namespace ActOneFuzzer; + +internal abstract class ActOneFuzzer +{ + private static void Main() + { + Fuzzer.OutOfProcess.Run(stream => + { + //pwsh ../../fuzz.ps1 ActOneFuzzer.csproj -i Testcases + try + { + using var memory = new MemoryStream(); + stream.CopyTo(memory); + var data = memory.ToArray().AsSpan(); + + var responder = new HandshakeState( + false, + ResponderValidKeysUtil.LocalStaticPrivateKey, + ResponderValidKeysUtil.LocalStaticPublicKey, + new FakeFixedKeyDh(ResponderValidKeysUtil.EphemeralPrivateKey) + ); + + var buffer = new byte[ProtocolConstants.MAX_MESSAGE_LENGTH]; + responder.ReadMessage(data, buffer); + } + catch (ObjectDisposedException) { } + catch (InvalidOperationException) { } + catch (ArgumentException) { } + catch (CryptographicException) { } + catch (FormatException) { } //Crash found: System.FormatException: Invalid public key at NBitcoin.PubKey..ctor(ReadOnlySpan`1 bytes) + }); + } +} \ No newline at end of file diff --git a/test/FuzzTests/NLightning.Bolts.BOLT8/ActOneFuzzer/Testcases/sample_input b/test/FuzzTests/NLightning.Bolts.BOLT8/ActOneFuzzer/Testcases/sample_input new file mode 100644 index 0000000..16cb829 Binary files /dev/null and b/test/FuzzTests/NLightning.Bolts.BOLT8/ActOneFuzzer/Testcases/sample_input differ diff --git a/test/FuzzTests/NLightning.Bolts.BOLT8/ActTwoFuzzer/ActTwoFuzzer.csproj b/test/FuzzTests/NLightning.Bolts.BOLT8/ActTwoFuzzer/ActTwoFuzzer.csproj new file mode 100644 index 0000000..e08d49a --- /dev/null +++ b/test/FuzzTests/NLightning.Bolts.BOLT8/ActTwoFuzzer/ActTwoFuzzer.csproj @@ -0,0 +1,19 @@ + + + + Exe + net8.0 + enable + enable + + + + + + + + + + + + diff --git a/test/FuzzTests/NLightning.Bolts.BOLT8/ActTwoFuzzer/Program.cs b/test/FuzzTests/NLightning.Bolts.BOLT8/ActTwoFuzzer/Program.cs new file mode 100644 index 0000000..ba9681d --- /dev/null +++ b/test/FuzzTests/NLightning.Bolts.BOLT8/ActTwoFuzzer/Program.cs @@ -0,0 +1,45 @@ +using System.Security.Cryptography; +using System.Text; +using NLightning.Bolts.BOLT8.Constants; +using NLightning.Bolts.BOLT8.States; +using NLightning.Bolts.Tests.BOLT8.Mock; +using NLightning.Bolts.Tests.BOLT8.Utils; +using SharpFuzz; + +namespace ActTwoFuzzer; + +internal abstract class ActTwoFuzzer +{ + private static void Main() + { + Fuzzer.OutOfProcess.Run(stream => + { + //pwsh ../../fuzz.ps1 ActTwoFuzzer.csproj -i Testcases + try + { + using var memory = new MemoryStream(); + stream.CopyTo(memory); + var data = memory.ToArray().AsSpan(); + + var initiator = new HandshakeState( + true, + InitiatorValidKeysUtil.LocalStaticPrivateKey, + InitiatorValidKeysUtil.RemoteStaticPublicKey, + new FakeFixedKeyDh(InitiatorValidKeysUtil.EphemeralPrivateKey) + ); + + var buffer = new byte[ProtocolConstants.MAX_MESSAGE_LENGTH]; + + initiator.WriteMessage(Encoding.ASCII.GetBytes(string.Empty), buffer); + + initiator.ReadMessage(data, buffer); + } + catch (ObjectDisposedException) { } + catch (InvalidOperationException) { } + catch (ArgumentException) { } + catch (CryptographicException) { } + catch (FormatException) { } //Crash found: System.FormatException: Invalid public key at NBitcoin.PubKey..ctor(ReadOnlySpan`1 bytes) + + }); + } +} \ No newline at end of file diff --git a/test/FuzzTests/NLightning.Bolts.BOLT8/ActTwoFuzzer/Testcases/sample_input b/test/FuzzTests/NLightning.Bolts.BOLT8/ActTwoFuzzer/Testcases/sample_input new file mode 100644 index 0000000..16cb829 Binary files /dev/null and b/test/FuzzTests/NLightning.Bolts.BOLT8/ActTwoFuzzer/Testcases/sample_input differ diff --git a/test/FuzzTests/fuzz.ps1 b/test/FuzzTests/fuzz.ps1 new file mode 100644 index 0000000..3e5d79b --- /dev/null +++ b/test/FuzzTests/fuzz.ps1 @@ -0,0 +1,63 @@ +param ( + [Parameter(Mandatory = $true)] + [string]$project, + [Parameter(Mandatory = $true)] + [string]$i, + [string]$x = $null, + [int]$t = 10000, + [string]$command = "sharpfuzz" +) + +Set-StrictMode -Version Latest + +$outputDir = "bin" +$findingsDir = "findings" + +if (Test-Path $outputDir) { + Remove-Item -Recurse -Force $outputDir +} + +if (Test-Path $findingsDir) { + Remove-Item -Recurse -Force $findingsDir +} + +dotnet publish $project -c release -o $outputDir + +$projectName = (Get-Item $project).BaseName +$projectDll = "$projectName.dll" +$project = Join-Path $outputDir $projectDll + +$exclusions = @( + "dnlib.dll", + "SharpFuzz.dll", + "SharpFuzz.Common.dll", + $projectDll +) + +$fuzzingTargets = Get-ChildItem $outputDir -Filter *.dll ` +| Where-Object { $_.Name -notin $exclusions } ` +| Where-Object { $_.Name -notlike "System.*.dll" } + +if (($fuzzingTargets | Measure-Object).Count -eq 0) { + Write-Error "No fuzzing targets found" + exit 1 +} + +foreach ($fuzzingTarget in $fuzzingTargets) { + Write-Output "Instrumenting $fuzzingTarget" + & $command $fuzzingTarget + + if ($LastExitCode -ne 0) { + Write-Error "An error occurred while instrumenting $fuzzingTarget" + exit 1 + } +} + +$env:AFL_SKIP_BIN_CHECK = 1 + +if ($x) { + afl-fuzz -i $i -o $findingsDir -t $t -m none -x $x dotnet $project +} +else { + afl-fuzz -i $i -o $findingsDir -t $t -m none dotnet $project +} \ No newline at end of file diff --git a/test/FuzzTests/install-fuzz-tools.sh b/test/FuzzTests/install-fuzz-tools.sh new file mode 100644 index 0000000..2c13203 --- /dev/null +++ b/test/FuzzTests/install-fuzz-tools.sh @@ -0,0 +1,17 @@ +#!/bin/sh +set -eux + +# Download and extract the latest afl-fuzz source package +wget http://lcamtuf.coredump.cx/afl/releases/afl-latest.tgz +tar -xvf afl-latest.tgz + +rm afl-latest.tgz +cd afl-2.52b/ + +# Install afl-fuzz +sudo make install +cd .. +rm -rf afl-2.52b/ + +# Install SharpFuzz.CommandLine global .NET tool +dotnet tool install --global SharpFuzz.CommandLine \ No newline at end of file diff --git a/test/NLightning.Bolts.Tests/AssemblyInfo.cs b/test/NLightning.Bolts.Tests/AssemblyInfo.cs index 62ff212..025a0ab 100644 --- a/test/NLightning.Bolts.Tests/AssemblyInfo.cs +++ b/test/NLightning.Bolts.Tests/AssemblyInfo.cs @@ -1,3 +1,5 @@ using System.Runtime.CompilerServices; -[assembly: InternalsVisibleTo("DynamicProxyGenAssembly2")] \ No newline at end of file +[assembly: InternalsVisibleTo("DynamicProxyGenAssembly2")] +[assembly: InternalsVisibleTo("ActOneFuzzer")] +[assembly: InternalsVisibleTo("ActTwoFuzzer")] \ No newline at end of file