using VoiceCat.Interop; namespace VoiceCat.App.Forms; /// /// TOFU server-identity confirmation. Shown only for VcTofuStatus.FirstConnect/Mismatch /// — never Matched (that's the silent-success "subsequent connects verify the pin" path /// docs/security.md describes; showing a dialog on every routine reconnect would be exactly /// the "overly chatty" experience this project avoids elsewhere too). /// DialogResult.OK = accept (and the caller should call ConfirmServerIdentity(true) and, for /// FirstConnect/Mismatch, the core persists the new pin); DialogResult.Cancel = reject. /// public partial class ServerIdentityDialog : Form { public ServerIdentityDialog(VcTofuStatus status, string certFingerprintHex, string identityDisplayHex) { InitializeComponent(); string formattedCertFp = FormatFingerprint(certFingerprintHex); string formattedIdentityFp = string.IsNullOrEmpty(identityDisplayHex) ? "(not yet available)" : FormatFingerprint(identityDisplayHex); if (status == VcTofuStatus.Mismatch) { Text = "WARNING: Server identity changed"; lblWarning.Text = "WARNING: This server's identity has CHANGED since you last connected.\r\n\r\n" + "This could mean the server was reinstalled, OR that someone is intercepting " + "your connection. If you did not expect this server's identity to change, " + "choose Cancel."; lblFingerprint.Text = $"New certificate fingerprint:\r\n{formattedCertFp}\r\n\r\n" + $"Server also identifies as:\r\n{formattedIdentityFp}"; btnAccept.Text = "&Trust the new identity anyway"; } else { Text = "New server — verify identity"; lblWarning.Text = "You have not connected to this server before. If you have verified its " + "fingerprint with the server operator through another channel, choose Trust " + "and connect. Otherwise, choose Cancel."; lblFingerprint.Text = $"Certificate fingerprint:\r\n{formattedCertFp}\r\n\r\n" + $"Server also identifies as:\r\n{formattedIdentityFp}"; btnAccept.Text = "&Trust and connect"; } } protected override void OnShown(EventArgs e) { base.OnShown(e); // Force a screen reader to read the warning text immediately on dialog activation, // rather than landing focus straight on a button and silently skipping it. lblWarning.Focus(); } private static string FormatFingerprint(string hex) { // Groups of 4 hex chars, like a product key — easier to read/dictate/compare than one // unbroken 64-character string. var groups = new List(); for (int i = 0; i < hex.Length; i += 4) groups.Add(hex.Substring(i, Math.Min(4, hex.Length - i))); return string.Join(' ', groups); } }