Re: prompt_password()

new topic     » goto parent     » topic index » view thread      » older message » newer message
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 thread      » older message » newer message

Search



Quick Links

User menu

Not signed in.

Misc Menu