jaiello has asked for the wisdom of the Perl Monks concerning the following question:

I need to generate a 4 digit PIN number. I want to use the a combo of three bits of information. Say I have three bits of data for a user. Data1, Data2 and Data3. These may be any length. I want to use these three bits of data to generate a 4 digit number. Each time I pass these bits of data through the algorithm, it must produce the same 4 digit number. I do not want to save the PIN number. I want to generate it dynamically each time. For example, a user gives me these three bits of data. I then generate a pin number and give that to them. Next time they give me the same three bits AND the pin number. Now I can generate the PIN number and compare what they gave me for a PIN number. I can now verify if they are the same user. I cannot manage to work out how to generate the 4 digit number from bits of data. For the sake of this question, lets say the data bits are: data1=iamgreat data2=my@email.com data3=ilikepie I would appreciate any help y'all can offer.
  • Comment on Need a wait to generate a 4 digit "PIN" number

Replies are listed 'Best First'.
Re: Need a wait to generate a 4 digit "PIN" number
by KurtSchwind (Chaplain) on Dec 16, 2007 at 03:33 UTC

    Have you thought about using CRC?

    You could concat the strings and take the CRC. CRC gives you a 32 bit number. You could just take the last 4 digits of that. The CRC on the same given string is always the same, so your last four digits would also be the same.

    Note: There is a small bias when reading just the last 4 digits of a 32 bit number you get a small bias in the spread. In other words, it's not a well distributed psuedo random set of 4 digits. But it's generally 'random enough' for anything but cryptography.

    --
    I used to drive a Heisenbergmobile, but every time I looked at the speedometer, I got lost.
Re: Need a wait to generate a 4 digit "PIN" number
by polettix (Vicar) on Dec 16, 2007 at 03:45 UTC
    This kinda smells of homework and you didn't show your efforts so far... so no code examples for now :)

    Concatenate the three "bits of data" in some way (e.g. join), calculate some hash function over it (e.g. MD5 using Digest::MD5) and extract an integer from this hash (e.g. unpack). Calculate the rest modulo 10000 (perlop, look for %) and use sprintf to get a 4-digits zero-padded representation -- you're done.

    Note that this does not guarantee that the combinations from 0000 to 9999 are covered fairly. Well, I actually don't know... I'm just not able/willing to demonstrate it.

    Flavio
    perl -ple'$_=reverse' <<<ti.xittelop@oivalf

    Io ho capito... ma tu che hai detto?

      This is not homework and I didn't have a code sample as I was trying to find a way to do this. I have the bits already concatenated. I have a hex MD5hash of the concatenated string.

      You gave me the thought to use a Solaris like SUM of the string. So I did this:

      $pinc=join("$data1","$data2","$data3"); $digest2 = md5_hex($pinc); $pin=unpack("%32C*",$digest2) % 65535; print "[$pinc],[$digest2],[$pin]";

      and I get:

      [ilikeyoumy@email.comilikepie] [be256d5090d0875e7812428991d70026] [1961]

      This seems to work.

      Do you have an opinion on this?

      Thanks!

        That could result in something larger than 9999 (up to 65535) and it could result in 1, 2, 3 digit pins (you'll never get 0123, but you will get 123).

        What's with quotes around variable names?
        Why start with the hex form?
        Why calculate a hash *and* a checksum?

        Using a MD5 hash will produce a good distribution of PINs.
        Solution using MD5 hash:

        my $pinc = join('', $data1, $data2, $data3); my $pin = sprintf('%04d', unpack('N', md5($pinc)) % 10000);

        Using CRC32 will *probably* produce a good distribution of PINs, but I don't personally know that.
        Using CRC32 will probably be faster than MD5, but it probably won't be noticeable unless you do millions of these in a row.
        Solution using CRC32:

        my $pinc = join('', $data1, $data2, $data3); my $pin = sprintf('%04d', unpack('%32C*', $pinc) % 10000);
Re: Need a wait to generate a 4 digit "PIN" number
by CountZero (Bishop) on Dec 16, 2007 at 19:41 UTC
    Actually, sending the three bits of data plus the PIN does not generate any extra security. Unless all this info is encrypted when sent, anyone listening in can capture the info and impersonate your users.

    Furthermore, you are using "security through obfuscation". By not publishing your pin-generation code / algorithm, you hope that no-one who somehow finds the three bits of data will be able to "guess" the pin-code. But really it would not take a genius to guess the "join + MD5 or CRC hash" method. So don't rely on such amateurish security.

    Actually, a secure link plus a user-id and password is sufficiently secure for most applications. The password is never stored on your system (so nobody can compromise the passwords even if they hack your database), just the result of a one-way trapdoor function (which is a function which has no inverse, so you cannot calculate the password from the result, other than by brute force).

    CountZero

    A program should be light and agile, its subroutines connected like a string of pearls. The spirit and intent of the program should be retained throughout. There should be neither too little or too much, neither needless loops nor useless variables, neither lack of structure nor overwhelming rigidity." - The Tao of Programming, 4.1 - Geoffrey James

      The PIN is dynamically generated after concatenating the three elements. Two of which are guessable but the third is a pass phrase that should not be easily guessable. I agree. If they know all three elements, then the pin is irrelevant. The PIN is not really a PIN but a term I used for folks to quickly grasp what I needed to create. The checksum is merely a way for me to easily verify that the bits of data are the same ones used originally. I have a stateless machine. So, user A enters three bits-o-data. I create a checksum and send it to user B. User B still needs to enter the same three bits-o-data plus the checksum. Since I am stateless don't store anything on my system, I calculate the checksum and compare it to the entered PIN. If they are the same, I am relatively confident the three bits are the same. I can now proceed to the next task. If not, I simply say they are not the same so something they entered is different.

      But really it would not take a genius to guess the "join + MD5 or CRC hash" method. So don't rely on such amateurish security.

      That's why one of the joined fields should be some secret value. The key/password, so to speak. It should never be an issue whether the algorithm is public or not. Only the key needs to be private.

      But you do have a point. If the user already has a key/password, why would he need a PIN too? It's just another password. Two passwords are not more secure than one.

        ikegami,
        Two passwords are not more secure than one.

        Well, when two pieces of information comprise a single password it can be more secure. For instance, certain facilities require:

        • Something you know (PIN)
        • Something you have (electronic badge)
        • Something you are (fingerprint or retina scan)

        I know this doesn't have a lot to do with the thread but when I read your reply I wanted to comment. A closer example would be one of those security token key chains which is constantly generating new passwords. When logging in, you must be looking at the currently generated password and add your pin to it to be authenticated. Having just the token isn't enough.

        Cheers - L~R