using System.Runtime.Versioning;
using DodoSSH.Client.App.Platform;
using DodoSSH.Client.Session;
namespace DodoSSH.Client.App.Tests;
///
/// The real TPM-backed store, as far as it can be exercised without a person. Which is not far.
///
///
///
/// Two tests, and the reason there are only two is a measured finding. A key created under
/// CngUIProtectionLevels.ProtectKey prompts at creation, not only at use: the policy means
/// "protect this key with a PIN", so Windows asks the user to set that up when the key is made. Sealing
/// therefore prompts as well as opening, even though sealing needs only the public half.
///
///
/// So anything that calls SaveAsync, TryLoadAsync with a blob present, or ForgetAsync
/// after a save will block a suite forever waiting for somebody to enter a PIN. That was found by writing
/// those tests and watching the run hang for ten minutes. They are gone; what is left is the two paths that
/// provably reach no dialog.
///
///
/// The rest of this store is verified by using the application, and that is not a gap this file can close —
/// a consent dialog needs hardware and a person by design. Disabling the UI policy to make it testable
/// would be testing a different class, and the one property worth having would be the property removed.
///
///
[SupportedOSPlatform("windows")]
public sealed class WindowsDeviceKeyStoreTests
{
private static CancellationToken Token => TestContext.Current.CancellationToken;
[Fact]
public async Task OnAMachineWithATpm_TheStoreOffersItself()
{
// IsSupported probes with a throwaway key carrying no UI policy, which is why this one is safe to
// run: no policy, no dialog. It is also the only honest availability test, because the platform
// provider reports itself present on machines where creating a key then fails.
SkipUnlessSupported();
var store = DesktopDeviceKeyStores.ForThisMachine(new ClientPaths(Path.GetTempPath()));
store.ShouldBeOfType();
(await store.IsAvailableAsync(Token)).ShouldBeTrue();
}
[Fact]
public async Task WithNothingSaved_LoadingReturnsNullWithoutPrompting()
{
// Reaches no dialog because it returns on the missing file, before touching the TPM at all. That is
// also what keeps a fresh machine's unlock screen quiet: it must not prompt for a key it has never
// been given. If this test ever hangs, that ordering has been lost.
SkipUnlessSupported();
var directory = Path.Combine(Path.GetTempPath(), $"dodossh-devicekey-{Guid.CreateVersion7():N}");
var store = new WindowsDeviceKeyStore(new ClientPaths(directory));
(await store.TryLoadAsync(Token)).ShouldBeNull();
// Nothing was created, so there is nothing to clean up — asserted, because a store that wrote a
// directory just to answer "no" would be leaving litter on every launch of an unregistered machine.
Directory.Exists(directory).ShouldBeFalse();
}
private static void SkipUnlessSupported()
{
var supported = OperatingSystem.IsWindows()
&& DesktopDeviceKeyStores.ForThisMachine(new ClientPaths(Path.GetTempPath()))
is WindowsDeviceKeyStore;
if (!supported)
{
Assert.Skip("This machine has no TPM the platform crypto provider will hold a key in.");
}
}
}