Wednesday, September 8, 2021

How to read a secret from command line

Summary: A sample of C# code illustrating how to read a masked secret value entered via a console.
The following function can be used to read a secret value, such as a password from a command line:
/// 
/// Reads a secret from command line masking the characters entered by the user.
/// 
/// 
/// Prompt to be displayed for the user (e.g. "Enter password: "). If not specified, the prompt will not be displayed.
/// 
/// 
/// The masking character.
/// 
/// 
/// The string entered by the user.
/// 
private static string GetSecret
(
    string prompt = null,
    char mask = '*'
)
{
    // Input codes requiring special handling.
    const int ENTER         = 13;
    const int BACKSPACE     = 8;
    const int CTRLBACKSPACE = 127;
    const int ESC           = 27;

    // Character codes that must be ignored.
    int[] FILTERED = { 0, 9, 10 /*, 32 space, if you care */ };

    var secret = new System.Collections.Generic.Stack();
    char chr = (char)0;

    // If the prompt was specified, show it to the user.
    if (!String.IsNullOrEmpty(prompt))
      Console.Write(prompt);
    
    // Continue reading entered keys until user presses ENTER or ESC.
    while (((chr = System.Console.ReadKey(true).KeyChar) != ENTER) && (chr != ESC))
    {
        if (chr == BACKSPACE)
        {
            if (secret.Count > 0)
            {
                System.Console.Write("\b \b");
                secret.Pop();
            }
        }
        else if (chr == CTRLBACKSPACE)
        {
            while (secret.Count > 0)
            {
                System.Console.Write("\b \b");
                secret.Pop();
            }
        }
        else if (chr == ESC)
        {
            while (secret.Count > 0)
            {
                System.Console.Write("\b \b");
                secret.Pop();
            }
        }
        else if (FILTERED.Count(x => chr == x) > 0)
        {
        }
        else
        {
            secret.Push((char)chr);
            System.Console.Write(mask);
        }
    }

    System.Console.WriteLine();

    return new string(secret.Reverse().ToArray());
}