1. prompt_password()

I had a need to prompt the user for a password on the console, and I was surprised to see that we didn't have this in the standard library already.

Can and/or should this be added?

public function prompt_password( sequence prompt = "Password: " ) 
-- prompt the user for some text, but do not display the characters 
 
    sequence answer = "" 
 
    puts( 1, prompt ) 
 
    atom key = wait_key() 
    while key != 13 do 
        -- exit on return 
 
        if key = 8 and length( answer ) then 
            -- backspace 
            answer = answer[1..$-1] 
        elsif key > 31 and key < 255 then 
            -- printable character 
            answer &= key 
        end if 
 
        key = wait_key() 
    end while 
 
    puts( 1, "\n" ) 
 
    return answer 
end function 

-Greg

new topic     » topic index » view message » categorize

2. Re: prompt_password()

My 2 cents - I'm in favor.

ghaberek said...

I had a need to prompt the user for a password on the console, and I was surprised to see that we didn't have this in the standard library already.

Can and/or should this be added?

public function prompt_password( sequence prompt = "Password: " ) 
-- prompt the user for some text, but do not display the characters 
 
    sequence answer = "" 
 
    puts( 1, prompt ) 
 
    atom key = wait_key() 
    while key != 13 do 
        -- exit on return 
 
        if key = 8 and length( answer ) then 
            -- backspace 
            answer = answer[1..$-1] 
        elsif key > 31 and key < 255 then 
            -- printable character 
            answer &= key 
        end if 
 
        key = wait_key() 
    end while 
 
    puts( 1, "\n" ) 
 
    return answer 
end function 

-Greg

new topic     » goto parent     » topic index » view message » categorize

3. Re: prompt_password()

I too think it's a good idea but maybe we should incorporate display of an asterisk ( * ) for each character typed? That will at least give the user some idea that things are happening and might save them from a erroneous password if, for example, their password is 6 characters but 5 * show on the screen.

Just my 2c worth ... your mileage DEFINITELY will vary. :)

new topic     » goto parent     » topic index » view message » categorize

4. Re: prompt_password()

tbohon said...

I too think it's a good idea but maybe we should incorporate display of an asterisk ( * ) for each character typed? That will at least give the user some idea that things are happening and might save them from a erroneous password if, for example, their password is 6 characters but 5 * show on the screen.

Just my 2c worth ... your mileage DEFINITELY will vary. :)

I agree ... maybe an optional string, defaulting to "*" , but allowing the application writer to decide what character makes sense.

In some cases, that string will be empty. Other tools, like SSH, do not show you how many characters you have typed for your password. This is simply a trade-off between ease-of-use and security.

new topic     » goto parent     » topic index » view message » categorize

5. Re: prompt_password()

ghaberek said...

Can and/or should this be added?

I agree in principle, but we should have one that is more secure than this. I'll work on one later today and post it for inspection.

new topic     » goto parent     » topic index » view message » categorize

6. Re: prompt_password()

DerekParnell said...

I'll work on one later today and post it for inspection.

And here it is ...

include std/graphics.e 
include std/math.e 
 
/* 

    Returns an encoded password based on the user's input.  
     
    The actual password itself is not returned so the value returned by 
    this function is safe to store because even if viewed, the original 
    password cannot be determined. Note that even passwords that are textually 
    similar will produce extremely different encoded values. 
     
    The returned value is a sequence of bytes. The number of bytes returned is 
    determined by the 'pwsize' parameter. By default this is 28. Regardless of 
    the actual size of the user's password, this function will always return 
    'pwsize' number of bytes. Note that the minimum 'pwsize' is 8. 
     
    As the user presses usable keys, the 'dispsym' parameter (a character) is 
    displayed for each keypress. By default this is the '*' character. If  
    'dispsym' is less than 1, no character displaying will occur. 
     
    When displaying characters, at most 'maxdisp' characters will be displayed, 
    regardless of how many characters are in the actual password. By default 
    this is set to 10. This allows users to have longer passwords than the 
    screen area would otherwise accommodate. 
     
    The 'quality' parameter, if set to a non-zero value, will cause return 
    value to consist of two sequences. The first element is the encoded 
    password, and the second is a representation of the types of characters 
    keyed by the user. 
        There will be one atom value per character entered. The floor() of this 
        value will be from 1 to 5, indicating the class of character. 
            1 = Lowercase alphabetic 
            2 = Uppercase alphabetic 
            3 = Digit 
            4 = Punctuation 
            5 = Non-displayable character (e.g. The ESCAPE and Ctrl-X characters) 
        If a value is not an integer, then the non-integer portion can be made 
        up of any combination (addition) of 0.4, 0.2, 0.1, and 0.05. 
            0.4 = 'alphabetically' adjacent to the previous key (eg. 'a' and 'b') 
            0.2 = 'horizontally' adjacent to the previous key (eg. 'a' and 's') 
            0.1 = 'vertically' adjacent to the previous key (eg. 'a' and 'z') 
            0.05 = Duplicate of previous key. 
        Examples: 
        --  'e' followed by 'd', would give a value for 'd' of 1.5,  
            because 'd' is lowercase and is alphabetically and vertically 
            adjacent to 'e'. 
        --  'M' followed by 'N', would give a value for 'N' of 2.6,  
            because 'N' is uppercase and is alphabetically and horizontally 
            adjacent to 'M'. 
         
    The caller can uses this information to determine an assessment of the 
    quality of the password supplied by the user. By default, the quality 
    information is not returned. 
     
*/ 
public function prompt_password( sequence prompt = "Password: ",  
                                 integer pwsize = 28,  
                                 integer dispsym = '*',  
                                 integer maxdisp = 10,  
                                 integer quality = 0  
                                )  
    sequence pos 
    sequence encodedpw = ""  
    sequence qkeys = "" 
    integer key 
    integer pnkey = -1 
    integer pkkey = -1 
    integer pdkey = -1 
 
 
    if pwsize < 8 then 
        pwsize = 8 
    end if 
         
    puts( 1, prompt )  
    if dispsym > 0 then 
        pos = get_position() 
    end if 
     
    while key != 13 with entry do  
        if key <= 255 then -- Ignore navigation keys etc ... 
             
            if key = 8 then 
                -- backspace  
                if length(encodedpw) > 0 then  
                    -- Remove last character keyed. 
                    encodedpw = encodedpw[1..$-1]  
                    if quality then 
                        qkeys = qkeys[1 .. $-1] 
                    end if 
                     
                    if dispsym > 0 then 
                        -- Fix up screen display. 
                        pos[2] -= 1 
                        position(pos[1], pos[2]) 
                        puts(1, ' ')                 
                        position(pos[1], pos[2]) 
                    end if 
                     
                end if 
            else 
                -- new character  
                -- Get a hash value based on both the new character and all 
                -- the previous characters, but only use the least significant 
                -- byte value of the result. Append this to the encoded string. 
                encodedpw &= and_bits(#FF, hash(key, key & encodedpw)) 
                 
                if quality then 
                    -- Ok, quality data was requested for this. 
                    integer nkey, kkey 
                    nkey = find(key, "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789`~!@#$%^&*()_-+={[}]|\\;:'\",<.>/?") 
                    if nkey = 0 then 
                        nkey = key + 94 
                    end if 
                     
                    if nkey <= 26 then 
                        qkeys &= 1  -- lowercase 
                         
                    elsif nkey <= 52 then 
                        qkeys &= 2  -- uppercase 
                         
                    elsif nkey <= 62 then 
                        qkeys &= 3  -- digit 
                         
                    elsif nkey <= 94 then 
                        qkeys &= 4  -- punctuation 
                         
                    else 
                        qkeys &= 5  -- non-displayable 
                         
                    end if 
 
                    if nkey = pnkey then 
                        -- Duplicate keys 
                        qkeys[$] += 0.05 
                    end if 
                                         
                    if abs(nkey - pnkey) = 1 then 
                        -- 'alphabetically' adjacent 
                        qkeys[$] += 0.4 
                    end if 
                    pnkey = nkey 
                     
                    -- Check horizontal proximity 
                    kkey = find(key, "`1234567890-=qwertyuiop[]\\asdfghjkl;'zxcvbnm,./~!@#$%^&*()_+QWERTYUIOP{}|ASDFGHJKL:\"ZXCVBNM<>?" & {17,23,5,18,20,25,21,9,15,16,27,29,28,-1,1,19,4,6,7,8,10,11,12,-1,26,24,3,22,2,14,13}) 
                    if kkey = 0 then 
                        kkey = key + 94 
                    end if 
                     
                    if abs(kkey - pkkey) = 1 then 
                        -- Horizontally adjacent 
                        qkeys[$] += 0.2 
                    end if 
                    pkkey = kkey 
                     
                    -- Check vertical proximity 
                    kkey = find(key, "`1qaz2wsx3edc4rfv5tgb6yhn7ujm8ik,9ol.0p;/-['=]\\~!QAZ@WSX#EDC$RFV%TGB^YHN&UJM*IK<(OL>)P:?_{\"+}|"& {17,1,26,-1,19,24,23,-1,5,4,3,-1,18,6,22,-1,20,7,2,-1,25,8,14,-1,21,10,13,-1,9,15,-1,11,16,-1,12}) 
                    if kkey = 0 then 
                        kkey = key + 94 
                    end if 
                     
                    if abs(kkey - pdkey) = 1 then 
                        -- Vertically adjacent 
                        qkeys[$] += 0.1 
                    end if 
                    pdkey = kkey 
                else 
                    key = 0 
                end if 
 
                if dispsym > 0 then 
                    -- Got to display something. 
                    position(pos[1], pos[2]) 
                    puts(1, dispsym) 
                    if length(encodedpw) < maxdisp then 
                        pos[2] += 1 
                    end if 
                end if 
            end if  
      
        end if 
    entry 
        key = wait_key()  
    end while  
 
          
     
    if length(encodedpw) > 0 then 
        -- Ok, I got something from the user, so I need to 
        -- make sure its long enough and churned enough to 
        -- make it hard to work backwards to the actual 
        -- password. 
        encodedpw = append(prepend(encodedpw, length(encodedpw)), pwsize) 
        loop do 
            encodedpw &= encodedpw  
            for i = 1 to length(encodedpw) do 
                encodedpw[i] = hash(encodedpw[$-i+1], encodedpw) 
            end for 
         
            encodedpw = and_bits(#FF, encodedpw) 
        until length(encodedpw) > pwsize 
        end loop 
        -- Just take from the righthand part of the encoded string. 
        encodedpw = encodedpw[$ - pwsize  .. $ - 1] 
    end if   
      
    if quality then 
        return {encodedpw, qkeys} 
    else 
        return encodedpw  
    end if 
end function  

Using this function with the default settings, a password of "password" encodes as

{215,177,114,108,140,170,165,109,80,182,144,175,243,161,118,186,40,182,218,51,89,228,191,112,111,2,174,87} 

and a password of "Password" encodes as

{159,107,83,106,92,120,183,62,157,229,128,11,34,183,168,237,41,177,124,52,117,219,31,157,146,218,206,3} 
which is not exactly guessable or simple to reverse engineer.

No doubt this function can be further improved.
Forked into: Password hashing // lack of cryptographic hash functions

new topic     » goto parent     » topic index » view message » categorize

7. Re: prompt_password()

DerekParnell said...
DerekParnell said...

I'll work on one later today and post it for inspection.

And here it is ...

include std/graphics.e 
include std/math.e 
 
/* 

    Returns an encoded password based on the user's input.  
     
    The actual password itself is not returned so the value returned by 
    this function is safe to store because even if viewed, the original 
*/ 

While I like this idea along with other features of this function (like the concept of having a password quality checker, or the ability to compress the display chars so the user can type in an extra-long password or pass phrase), I don't think this the right place to deal with hashing the password.

In many cases, the application ask the user for the password, then pass it on to another (e.g. telnet, FTP, HTTP basic AUTH). In other cases, the application will have to hash or encrypt the raw value of the password in a certain way and then pass that hash on (e.g. SSH, HTTP Digest AUTH) and if prompt_password is returning the password with the wrong hash, then that's the end of that. The inability of this function to accommodate that makes it of significantly limited utility imvho.

While I like the idea of the quality checker, I think the implementation is quite raw. There's no simple length checking (though this can be determined by the other information returned by the quality checker), and there's no protection against using dictionary words or too common passwords ala cracklib.

new topic     » goto parent     » topic index » view message » categorize

8. Re: prompt_password()

jimcbrown2 said...

While I like this idea along with other features of this function (like the concept of having a password quality checker, or the ability to compress the display chars so the user can type in an extra-long password or pass phrase), I don't think this the right place to deal with hashing the password.

Yes, I think these things should be split out as separate functions. Is there enough here to justify a separate include file?

Matt

new topic     » goto parent     » topic index » view message » categorize

9. Re: prompt_password()

mattlewis said...
jimcbrown2 said...

While I like this idea along with other features of this function (like the concept of having a password quality checker, or the ability to compress the display chars so the user can type in an extra-long password or pass phrase), I don't think this the right place to deal with hashing the password.

Yes, I think these things should be split out as separate functions. Is there enough here to justify a separate include file?

Matt

I feel that there is. Actually, we've always been weak on the security / crypto side of things.

new topic     » goto parent     » topic index » view message » categorize

10. Re: prompt_password()

Ok, if one is collecting a password on behalf of another application, then all bets are off. The system is innately insecure in that situation.

An unsecure, dumbed-down version could more like this ...

include std/graphics.e 
 
/* 

    Returns the user's input.  
     
    The returned value is a sequence of bytes. 
     
    As the user presses usable keys, the 'dispsym' parameter (a character) is 
    displayed for each keypress. By default this is the '*' character. If  
    'dispsym' is less than 1, no character displaying will occur. 
     
    When displaying characters, at most 'maxdisp' characters will be displayed, 
    regardless of how many characters are in the actual password. By default 
    this is set to 10. This allows users to have longer passwords than the 
    screen area would otherwise accommodate. 
     
*/ 
public function prompt_password( sequence prompt = "Password: ",  
                                 integer dispsym = '*',  
                                 integer maxdisp = 10 
                                )  
    sequence pos 
    sequence user_input = ""  
    integer key 
 
    puts( 1, prompt )  
    if dispsym > 0 then 
        pos = get_position() 
    end if 
     
    while key != 13 with entry do  
        if key <= 255 then -- Ignore navigation keys etc ... 
             
            if key = 8 then 
                -- backspace  
                if length(user_input) > 0 then  
                    -- Remove last character keyed. 
                    user_input = user_input[1..$-1]  
                     
                    if dispsym > 0 then 
                        -- Fix up screen display. 
                        pos[2] -= 1 
                        position(pos[1], pos[2]) 
                        puts(1, ' ')                 
                        position(pos[1], pos[2]) 
                    end if 
                     
                end if 
            else 
                -- new character  
                user_input &= key 
 
                if dispsym > 0 then 
                    -- Got to display something. 
                    position(pos[1], pos[2]) 
                    puts(1, dispsym) 
                    if length(user_input) < maxdisp then 
                        pos[2] += 1 
                    end if 
                end if 
            end if  
      
        end if 
    entry 
        key = wait_key()  
    end while  
     
    return user_input  
 
end function  
new topic     » goto parent     » topic index » view message » categorize

11. Re: prompt_password()

Derek,

dispsym should be >= 32, since ASCII values 0-31 are non-printable. blink

-Greg

new topic     » goto parent     » topic index » view message » categorize

12. Re: prompt_password()

ghaberek said...

Derek,

dispsym should be >= 32, since ASCII values 0-31 are non-printable. blink

-Greg

Who says they have to be printable? I'm pretty sure that characters 1 to 31 have displayable glyphs in Windows and Unix terminals.

new topic     » goto parent     » topic index » view message » categorize

13. Re: prompt_password()

DerekParnell said...

Who says they have to be printable? I'm pretty sure that characters 1 to 31 have displayable glyphs in Windows and Unix terminals.

Well, I think it's generally understood that passwords should be only valid alphanumeric (or extended/accented) characters.

I don't think TAB or ESC should be valid password characters. And it's impossible to enter BEL or ACK on a keyboard.

Just my 2¢. getlost

-Greg

new topic     » goto parent     » topic index » view message » categorize

14. Re: prompt_password()

ghaberek said...
DerekParnell said...

Who says they have to be printable? I'm pretty sure that characters 1 to 31 have displayable glyphs in Windows and Unix terminals.

Well, I think it's generally understood that passwords should be only valid alphanumeric (or extended/accented) characters.

I don't think TAB or ESC should be valid password characters. And it's impossible to enter BEL or ACK on a keyboard.

Just my 2¢. getlost

-Greg

The dispsym is the character displayed on the screen and has nothing to do with the characters contained in the password.

Some unix systems even allow the BACKSPACE character as a valid password character.

BEL is Ctrl-G and ACK is CTRL-F.

new topic     » goto parent     » topic index » view message » categorize

15. Re: prompt_password()

The thread above is fine, but may I add another idea I use for my password system?

It is simply to display a random lower-case alpha for each character typed rather than an asterisk. The only reason for asterisks is to hide the password from a snooper; if random letters are shown the snooper will immediately see those and think they are the password, and so not look at the keyboard being used, or, if looked at at a later date, the snooper will assume that the alphas MUST be right or at least close to the truth. Which is, of course, as far from the truth as you can get.

This is of course dead simple to implement.

new topic     » goto parent     » topic index » view message » categorize

16. Re: prompt_password()

The encryption program I am writing is a windows program, so I don't if it applies directly to this discussion.
The way I am handling passwords is to use normal EditText fields and intercept the keys as they are typed.
The program has a number of Ctrl and Alt hot keys, so I did not want to use any modified characters in the passwords. This limited the number of characters that could be entered to approximatly 95 from a standard windows keyboard. This was not acceptable, so what I done was added a toggle to turn ON/OFF an extended character set using Ctrl+E or by a popup menu. When extended characters is on, I remap characters from 32 to 127 to two different groups in the 128 to 255 range, so typing 'a' with extended on enters a keycode 227 and 'A' enters 159 and so on. This allows 190 characters using only the normal 'typewriter' type keys.
There is also a Hide/Show characters toggle (Ctrl+*) to show the actual characters typed or show all asterisks. 'Show characters' can be toggled off whether the fields are empty or not, however it cannot not be toggled on unless both password fields are empty.
The fields allow 1 to 32 character passwords, I don't want to police minimum password lengths, so I leave it up to the user to enter secure passwords.
All passwords are expanded to 32 characters during the encryption process.

More to the point, how about expanding the dispsym flag to display either astericks or random characters, such as
integer dispsym = "*" for astericks or
integer dispsym = "r" for random, as Andy suggested

and another flag to return the raw characters typed or a more secure encoded password, such as
integer dispsym = "r" for raw or
integer dispsym = "e" for encoded as Derek suggested

new topic     » goto parent     » topic index » view message » categorize

17. Re: prompt_password()

Slight correction, the return flag would be something like returnchars not dispsym

new topic     » goto parent     » topic index » view message » categorize

Search



Quick Links

User menu

Not signed in.

Misc Menu