Repository: PowerShell/PowerShell
File: src/Microsoft.PowerShell.ConsoleHost/host/msh/ConsoleHostUserInterfaceSecurity.cs (C#)

Code before:
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

using System;
using System.Globalization;
using System.Management.Automation;
using System.Management.Automation.Internal;
using System.Security;

using Microsoft.Win32;

namespace Microsoft.PowerShell
{
    /// <summary>
    /// ConsoleHostUserInterface implements console-mode user interface for powershell.
    /// </summary>
    internal partial
    class ConsoleHostUserInterface : System.Management.Automation.Host.PSHostUserInterface
    {
        /// <summary>
        /// Prompt for credentials.
        ///
        /// In future, when we have Credential object from the security team,
        /// this function will be modified to prompt using secure-path
        /// if so configured.
        /// </summary>
        /// <param name="userName">Name of the user whose creds are to be prompted for. If set to null or empty string, the function will prompt for user name first.</param>
        /// <param name="targetName">Name of the target for which creds are being collected.</param>
        /// <param name="message">Message to be displayed.</param>
        /// <param name="caption">Caption for the message.</param>
        /// <returns>PSCredential object.</returns>

        public override PSCredential PromptForCredential(
            string caption,
            string message,
            string userName,
            string targetName)
        {
            return PromptForCredential(caption,
                                         message,
                                         userName,
                                         targetName,
                                         PSCredentialTypes.Default,
                                         PSCredentialUIOptions.Default);
        }

        /// <summary>
        /// Prompt for credentials.
        /// </summary>
        /// <param name="userName">Name of the user whose creds are to be prompted for. If set to null or empty string, the function will prompt for user name first.</param>
        /// <param name="targetName">Name of the target for which creds are being collected.</param>
        /// <param name="message">Message to be displayed.</param>
        /// <param name="caption">Caption for the message.</param>
        /// <param name="allowedCredentialTypes">What type of creds can be supplied by the user.</param>
        /// <param name="options">Options that control the cred gathering UI behavior.</param>
        /// <returns>PSCredential object, or null if input was cancelled (or if reading from stdin and stdin at EOF).</returns>

        public override PSCredential PromptForCredential(
            string caption,
            string message,
            string userName,
            string targetName,
            PSCredentialTypes allowedCredentialTypes,
            PSCredentialUIOptions options)
        {
            PSCredential cred = null;
            SecureString password = null;
            string userPrompt = null;
            string passwordPrompt = null;

            if (!string.IsNullOrEmpty(caption))
            {
                // Should be a skin lookup

                WriteLineToConsole();
                WriteLineToConsole(PromptColor, RawUI.BackgroundColor, WrapToCurrentWindowWidth(caption));
            }

            if (!string.IsNullOrEmpty(message))
            {
                WriteLineToConsole(WrapToCurrentWindowWidth(message));
            }

            if (string.IsNullOrEmpty(userName))
            {
                userPrompt = ConsoleHostUserInterfaceSecurityResources.PromptForCredential_User;

                //
                // need to prompt for user name first
                //
                do
                {
                    WriteToConsole(userPrompt, true);
                    userName = ReadLine();
                    if (userName == null)
                    {
                        return null;
                    }
                }
                while (userName.Length == 0);
            }

            passwordPrompt = StringUtil.Format(ConsoleHostUserInterfaceSecurityResources.PromptForCredential_Password, userName
            );

            //
            // now, prompt for the password
            //
            WriteToConsole(passwordPrompt, true);
            password = ReadLineAsSecureString();
            if (password == null)
            {
                return null;
            }

            WriteLineToConsole();

            cred = new PSCredential(userName, password);

            return cred;
        }
    }
}



Review comment (suggestion):
We have no need to make the copy. See https://www.sjoerdlangkemper.nl/2017/11/08/comparing-securestrings-in-dotnet/
```c#
static bool IsEqual(IntPtr bstr1, IntPtr bstr2)
{
    var length1 = Marshal.ReadInt32(bstr1, -4);
    var length2 = Marshal.ReadInt32(bstr2, -4);

    if (length1 != length2) return false;

    var equal = 0;
    for (var i = 0; i < length1; i++)
    {
        var c1 = Marshal.ReadByte(bstr1 + i);
        var c2 = Marshal.ReadByte(bstr2 + i);
        equal |= c1 ^ c2;
    }
    return equal == 0;
}
```

Code after:
                    WriteToConsole(ConsoleColor.Red, ConsoleColor.Black, passwordMismatch, false);
                    return null;
                }
            }

            WriteLineToConsole();
            cred = new PSCredential(userName, password);
            return cred;
        }

        private static bool SecureStringEquals(SecureString password, SecureString confirmPassword)
        {
            if (password.Length != confirmPassword.Length)
            {
                return false;
            }

            IntPtr pwd_ptr = IntPtr.Zero;
            IntPtr confirmPwd_ptr = IntPtr.Zero;
            try
            {
                pwd_ptr = Marshal.SecureStringToBSTR(password);
                if (pwd_ptr == IntPtr.Zero)
                {
                    return false;
                }

                confirmPwd_ptr = Marshal.SecureStringToBSTR(confirmPassword);
                if (confirmPwd_ptr == IntPtr.Zero)
                {
                    return false;
                }

                int pwdLength = Marshal.ReadInt32(pwd_ptr, -4);
                int equal = 0;
                for (int i = 0; i < pwdLength; i++)
                {
                    byte c1 = Marshal.ReadByte(pwd_ptr, i);
                    byte c2 = Marshal.ReadByte(confirmPwd_ptr, i);
                    equal = c1 ^ c2;
                    if (equal != 0)
                    {
                        return false;
                    }
                }

                return true;
            }
            finally
            {
                if (pwd_ptr != IntPtr.Zero)