btcpayserver/BTCPayServer/SSH/SSHFingerprint.cs

93 lines
3.0 KiB
C#

using System;
using System.Globalization;
using System.Linq;
using NBitcoin;
using NBitcoin.DataEncoders;
namespace BTCPayServer.SSH
{
public class SSHFingerprint
{
public static bool TryParse(string str, out SSHFingerprint fingerPrint)
{
ArgumentNullException.ThrowIfNull(str);
fingerPrint = null;
str = str.Trim();
try
{
var shortFingerprint = str.Replace(":", "", StringComparison.OrdinalIgnoreCase);
if (HexEncoder.IsWellFormed(shortFingerprint))
{
var hash = Encoders.Hex.DecodeData(shortFingerprint);
if (hash.Length == 16)
{
fingerPrint = new SSHFingerprint(hash);
return true;
}
return false;
}
}
catch
{
}
if (str.StartsWith("SHA256:", StringComparison.OrdinalIgnoreCase))
str = str.Substring("SHA256:".Length).Trim();
if (str.Contains(':', StringComparison.OrdinalIgnoreCase))
return false;
if (!str.EndsWith('='))
str = str + "=";
try
{
var hash = Encoders.Base64.DecodeData(str);
if (hash.Length == 32)
{
fingerPrint = new SSHFingerprint(hash);
return true;
}
}
catch
{
}
return false;
}
public SSHFingerprint(byte[] hash)
{
if (hash.Length == 16)
{
_ShortFingerprint = hash;
_Original = string.Join(':', hash.Select(b => b.ToString("x2", CultureInfo.InvariantCulture))
.ToArray());
}
else if (hash.Length == 32)
{
_FullHash = hash;
_Original = "SHA256:" + Encoders.Base64.EncodeData(hash);
if (_Original.EndsWith("=", StringComparison.OrdinalIgnoreCase))
_Original = _Original.Substring(0, _Original.Length - 1);
}
else
throw new ArgumentException(paramName: nameof(hash), message: "Invalid length, expected 16 or 32");
}
readonly byte[] _ShortFingerprint;
readonly byte[] _FullHash;
public bool Match(byte[] shortFingerprint, byte[] hostKey)
{
ArgumentNullException.ThrowIfNull(shortFingerprint);
ArgumentNullException.ThrowIfNull(hostKey);
if (_ShortFingerprint != null)
return Utils.ArrayEqual(shortFingerprint, _ShortFingerprint);
return Utils.ArrayEqual(_FullHash, NBitcoin.Crypto.Hashes.SHA256(hostKey));
}
readonly string _Original;
public override string ToString()
{
return _Original;
}
}
}