diff --git a/NRustLightning.sln.DotSettings b/NRustLightning.sln.DotSettings index 6b64ee1..cc462c9 100644 --- a/NRustLightning.sln.DotSettings +++ b/NRustLightning.sln.DotSettings @@ -1,6 +1,7 @@  RPC SQ + True True True True diff --git a/src/NRustLightning.CLI/ChannelTools/ChanTools.cs b/src/NRustLightning.CLI/ChannelTools/ChanTools.cs new file mode 100644 index 0000000..e7fa80a --- /dev/null +++ b/src/NRustLightning.CLI/ChannelTools/ChanTools.cs @@ -0,0 +1,22 @@ +using System.CommandLine; + +namespace NRustLightning.CLI.ChannelTools +{ + public class ChanTools + { + /// + /// Get common options for `chantools` subcommand. + /// + /// + public Option[] GetOptions() => + new Option[] + { + new Option(""), + }; + + public Command ChanBackup() + { + var opts = + } + } +} \ No newline at end of file diff --git a/src/NRustLightning.CLI/CommandLineUtils.cs b/src/NRustLightning.CLI/CommandLineUtils.cs new file mode 100644 index 0000000..5e666e0 --- /dev/null +++ b/src/NRustLightning.CLI/CommandLineUtils.cs @@ -0,0 +1,51 @@ +using System; +using System.Text; +using System.Text.RegularExpressions; +using DotNetLightning.Crypto; +using Microsoft.FSharp.Core; +using NBitcoin; +using NRustLightning.Infrastructure.Utils; + +namespace NRustLightning.CLI +{ + public static class CommandLineUtils + { + private static Regex NumberDotsRegex = new Regex("[\\d.\\-\\n\\r\\t]*"); + private static Regex MultipleSpaces = new Regex(" [ ]+"); + + public static bool TryReadAezeedFromTerminal(out ExtKey? rootKey) + { + rootKey = null; + string? mnemonicStr = null; + while (string.IsNullOrEmpty(mnemonicStr)) + { + Console.WriteLine("Input your 24-word mnemonic separated by spaces: "); + mnemonicStr = Console.ReadLine()?.Trim().ToLower(); + } + + // To allow the tool to also accept the copy/pasted version of the backup text (which contains numbers and + // dts and multiple spaces), we do some more cleanup with regex. + mnemonicStr = + NumberDotsRegex.Replace(mnemonicStr, ""); + mnemonicStr = MultipleSpaces.Replace(mnemonicStr, " ").Trim(); + + var mnemonics = new Mnemonic(mnemonicStr); + Console.WriteLine(); + if (mnemonics.Words.Length != 24) + { + Console.WriteLine($"wrong cipher seed. mnemonic length: got {mnemonics.Words.Length}, expecting {24} words"); + return false; + } + var seedPassphrase = CliUtils.GetNewSeedPass(); + var r = mnemonics.ToCipherSeed(Encoding.UTF8.GetBytes(seedPassphrase), null); + if (r.IsError) + { + Console.WriteLine(r.ErrorValue); + return false; + } + var cipherSeed = r.ResultValue; + rootKey = new ExtKey(cipherSeed.Entropy.AsSpan()); + return true; + } + } +} \ No newline at end of file diff --git a/src/NRustLightning.Infrastructure/Configuration/Config.cs b/src/NRustLightning.Infrastructure/Configuration/Config.cs index 1649f32..40f4055 100644 --- a/src/NRustLightning.Infrastructure/Configuration/Config.cs +++ b/src/NRustLightning.Infrastructure/Configuration/Config.cs @@ -37,7 +37,6 @@ public class Config private string? _seedFromConfig; - private string? _pin; public NRustLightningNetworkProvider NetworkProvider { get; set; } @@ -139,7 +138,6 @@ public Config LoadArgs(IConfiguration config, ILogger? logger) DBCacheMB = config.GetOrDefault("dbcache", Constants.DefaultDBCacheMB); _seedFromConfig = config.GetOrDefault("seed", string.Empty); - _pin = config.GetOrDefault("pin", string.Empty); SeedFilePath = Path.Join(DataDir, "node_secret"); return this; } @@ -160,40 +158,5 @@ public string GetSeedInConfig() return _seedFromConfig; } - public string Pin => _pin; - - public string GetNewPin() - { - string pin = _pin; - if (!string.IsNullOrEmpty(pin)) return pin; - - Console.WriteLine("========================================================================"); - Console.WriteLine("Please enter the pin code to secure your seed on disk "); - Console.WriteLine("It is recommended to be longer than 10 characters to be cryptographically secure"); - Console.WriteLine("Please do not forget this pin code. You will need it when restarting the server"); - Console.WriteLine("========================================================================"); - while (true) - { - Console.WriteLine("Please enter your pin code: "); - var pin1 = Console.ReadLine(); - Console.WriteLine("Please enter again:"); - var pin2 = Console.ReadLine(); - if (pin1 == pin2 && !string.IsNullOrEmpty(pin1)) - { - pin = pin1; - break; - } - else if (pin1 == pin2) - { - Console.WriteLine("You cannot specify empty pin code"); - } - else - { - Console.WriteLine("pin code mismatch! try again"); - } - } - - return pin; - } } } \ No newline at end of file diff --git a/src/NRustLightning.Infrastructure/Utils/CliUtils.cs b/src/NRustLightning.Infrastructure/Utils/CliUtils.cs new file mode 100644 index 0000000..3828f4b --- /dev/null +++ b/src/NRustLightning.Infrastructure/Utils/CliUtils.cs @@ -0,0 +1,87 @@ +using System; +using DotNetLightning.Crypto; +using NRustLightning.Infrastructure.Configuration; + +namespace NRustLightning.Infrastructure.Utils +{ + public static class CliUtils + { + public static bool AskUserYesOrNo(bool? defaultAnswer = null) + { + while (true) + { + var input = Console.ReadLine(); + if (input.IsYes()) + return true; + if (input.IsNo()) + return false; + if (defaultAnswer != null) + return defaultAnswer.Value; + Console.WriteLine("Please answer by yes or no"); + } + } + + public static string GetNewSeedPass() + { + string? pin; + Console.WriteLine("========================================================================"); + Console.WriteLine("Please enter the cipher seed passphrase to secure your seed on disk "); + Console.WriteLine("It is recommended to be longer than 10 characters to be cryptographically secure"); + Console.WriteLine("Please do not forget this cipher seed passphrase. You will need it when restarting" + + "the server"); + Console.WriteLine("========================================================================"); + while (true) + { + Console.WriteLine("Please enter your cipher seed passphrase: "); + var pin1 = Console.ReadLine(); + if (string.IsNullOrEmpty(pin1?.Trim())) + { + Console.WriteLine($"Are you sure you want to use empty pin code? [y/N]"); + var isYes = CliUtils.AskUserYesOrNo(false); + if (isYes) + return ""; + else + continue; + } + Console.WriteLine("Please enter again:"); + var pin2 = Console.ReadLine(); + if (pin1 == pin2) + { + pin = pin1; + break; + } + else + { + Console.WriteLine("cipher seed passphrase mismatch! try again"); + } + } + return pin; + } + + public static string GetMnemonic() + { + } + + public static string GetSeedPass(byte[] encipheredCipherSeed) + { + if (encipheredCipherSeed.Length != 33) + throw new ConfigException($"{nameof(encipheredCipherSeed)} must be length 33! It was {encipheredCipherSeed.Length}"); + string? seedPass; + bool isValid; + while (true) + { + Console.WriteLine("Please enter your cipher seed passphrase: "); + var pin1 = Console.ReadLine(); + Console.WriteLine("Please enter again:"); + var pin2 = Console.ReadLine(); + if (pin1 == pin2) + { + seedPass = pin1; + break; + } + Console.WriteLine("cipher seed passphrase mismatch! try again"); + } + return seedPass; + } + } +} \ No newline at end of file diff --git a/src/NRustLightning.Infrastructure/Utils/Extensions.cs b/src/NRustLightning.Infrastructure/Utils/Extensions.cs new file mode 100644 index 0000000..4d6c7b4 --- /dev/null +++ b/src/NRustLightning.Infrastructure/Utils/Extensions.cs @@ -0,0 +1,26 @@ +using System; + +namespace NRustLightning.Infrastructure.Utils +{ + public static class Extensions + { + public static bool IsYes(this string userInput) => userInput switch + { + "yes" => true, + "Yes" => true, + "y" => true, + "Y" => true, + _ => false + }; + + public static bool IsNo(this string userInput) => userInput switch + { + "no" => true, + "No" => true, + "n" => true, + "N" => true, + _ => false + }; + + } +} \ No newline at end of file diff --git a/src/NRustLightning.Infrastructure/Utils/Utils.cs b/src/NRustLightning.Infrastructure/Utils/Utils.cs index f831c66..978a47f 100644 --- a/src/NRustLightning.Infrastructure/Utils/Utils.cs +++ b/src/NRustLightning.Infrastructure/Utils/Utils.cs @@ -1,3 +1,4 @@ +using System; using System.Diagnostics; using System.Runtime.CompilerServices; using NRustLightning.Infrastructure.Models.Response; @@ -18,5 +19,6 @@ public static T Fail(string msg) where T : class { throw Fail(msg); } + } } \ No newline at end of file diff --git a/src/NRustLightning.Server/Program.cs b/src/NRustLightning.Server/Program.cs index 8ee6a33..088729f 100644 --- a/src/NRustLightning.Server/Program.cs +++ b/src/NRustLightning.Server/Program.cs @@ -4,11 +4,9 @@ using System.CommandLine.Invocation; using System.CommandLine.Parsing; using System.IO; -using System.Linq; using System.Net; using System.Security.Cryptography.X509Certificates; using System.Threading.Tasks; -using DotNetLightning.Chain; using Microsoft.AspNetCore.Connections; using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Server.Kestrel.Core; @@ -17,11 +15,12 @@ using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; + +using DotNetLightning.Crypto; using NBitcoin; using NRustLightning.Infrastructure.Configuration; -using NRustLightning.Infrastructure.Configuration.SubConfiguration; using NRustLightning.Infrastructure.Repository; -using NRustLightning.Interfaces; +using NRustLightning.Infrastructure.Utils; using NRustLightning.Server.Configuration; using NRustLightning.Server.P2P; using IKeysRepository = NRustLightning.Infrastructure.Interfaces.IKeysRepository; @@ -50,10 +49,10 @@ public static async Task Main(string[] args) else { var maybeEncryptedSeed = await conf.Value.TryGetEncryptedSeed(); + var pin = CliUtils.GetNewSeedPass(); if (maybeEncryptedSeed != null) { var isValidPin = false; - var pin = conf.Value.Pin; var maybeErrMsg = repo.InitializeFromEncryptedSeed(maybeEncryptedSeed, pin); if (maybeErrMsg is null) { @@ -82,8 +81,7 @@ public static async Task Main(string[] args) else { logger.LogWarning($"seed not found in {conf.Value.SeedFilePath}! generating new seed..."); - var pin = conf.Value.GetNewPin(); - var seed = RandomUtils.GetUInt256().ToString(); + var seed = CipherSeed.Create(); repo.InitializeFromSeed(seed); await repo.EncryptSeedAndSaveToFile(seed, pin); } diff --git a/src/NRustLightning/Interfaces/IBroadcaster.cs b/src/NRustLightning/Interfaces/IBroadcaster.cs index 6431339..e4f1ec8 100644 --- a/src/NRustLightning/Interfaces/IBroadcaster.cs +++ b/src/NRustLightning/Interfaces/IBroadcaster.cs @@ -19,7 +19,7 @@ internal struct BroadcasterDelegatesHolder : IDisposable private readonly IBroadcaster _broadcaster; private readonly Network _n; private readonly BroadcastTransaction _broadcastTransaction; - public BroadcasterDelegatesHolder(IBroadcaster broadcaster, NBitcoin.Network n) + public BroadcasterDelegatesHolder(IBroadcaster broadcaster, Network n) { _broadcaster = broadcaster ?? throw new ArgumentNullException(nameof(broadcaster)); _n = n ?? throw new ArgumentNullException(nameof(n));