How to generate a verification code/number?

2019-01-21 05:36发布

I'm working on an application where users have to make a call and type a verification number with the keypad of their phone.

I would like to be able to detect if the number they type is correct or not. The phone system does not have access to a list of valid numbers, but instead it will validate the number against an algorithm (like a credit card number).

Here are some of the requirements :

  • It must be difficult to type a valid random code
  • It must be difficult to have a valid code if I make a typo (tranposition of digits, wrong digit)
  • I must have a reasonnable number of possible combinations (let's say 1M)
  • The code must be as short as possible, to avoid errors from the user

Given these requirements, how would you generate such a number ?

EDIT :

@Haaked : The code has to be numerical, because the user type it with it's phone.

@matt b : On the first step, the code is displayed on a Web page, the second step is to call and type in the code. I don't know the user's phone number.

Folowup : I've found several algorithms to check the validity of numbers (See this intersting Google Code project : checkDigits).

9条回答
兄弟一词,经得起流年.
2楼-- · 2019-01-21 06:20

For 1M combinations you'll need 6 digits. To make sure that there aren't any accidentally valid codes, I suggest 9 digits with a 1/1000 chance that a random code works. I'd also suggest using another digit (10 total) to perform an integrity check. As far as distribution patterns, random will suffice and the check digit will ensure that a single error will not result in a correct code.

Edit: Apparently I didn't fully read your request. Using a credit card number, you could perform a hash on it (MD5 or SHA1 or something similar). You then truncate at an appropriate spot (for example 9 characters) and convert to base 10. Then you add the check digit(s) and this should more or less work for your purposes.

查看更多
ら.Afraid
3楼-- · 2019-01-21 06:25

It sounds like you have the unspoken requirement that it must be quickly determined, via algorithm, that the code is valid. This would rule out you simply handing out a list of one time pad numbers.

There are several ways people have done this in the past.

  1. Make a public key and private key. Encode the numbers 0-999,999 using the private key, and hand out the results. You'll need to throw in some random numbers to make the result come out to the longer version, and you'll have to convert the result from base 64 to base 10. When you get a number entered, convert it back to base64, apply the private key, and see if the intereting numbers are under 1,000,000 (discard the random numbers).
  2. Use a reversible hash function
  3. Use the first million numbers from a PRN seeded at a specific value. The "checking" function can get the seed, and know that the next million values are good. It can either generate them each time and check one by one when a code is received, or on program startup store them all in a table, sorted, and then use binary search (maximum of compares) since one million integers is not a whole lot of space.

There are a bunch of other options, but these are common and easy to implement.

-Adam

查看更多
ら.Afraid
4楼-- · 2019-01-21 06:28

Does it have to be only numbers? You could create a random number between 1 and 1M (I'd suggest even higher though) and then Base32 encode it. The next thing you need to do is Hash that value (using a secret salt value) and base32 encode the hash. Then append the two strings together, perhaps separated by the dash.

That way, you can verify the incoming code algorithmically. You just take the left side of the code, hash it using your secret salt, and compare that value to the right side of the code.

查看更多
Explosion°爆炸
5楼-- · 2019-01-21 06:29

After some research, I think I'll go with the ISO 7064 Mod 97,10 formula. It seems pretty solid as it is used to validate IBAN (International Bank Account Number).

The formula is very simple:

  1. Take a number : 123456
  2. Apply the following formula to obtain the 2 digits checksum : mod(98 - mod(number * 100, 97), 97) => 76
  3. Concat number and checksum to obtain the code => 12345676
  4. To validate a code, verify that mod(code, 97) == 1

Test :

  • mod(12345676, 97) = 1 => GOOD
  • mod(21345676, 97) = 50 => BAD !
  • mod(12345678, 97) = 10 => BAD !

Apparently, this algorithm catches most of the errors.

Another interesting option was the Verhoeff algorithm. It has only one verification digit and is more difficult to implement (compared to the simple formula above).

查看更多
混吃等死
6楼-- · 2019-01-21 06:29

Assuming you already know how to detect which key the user hit, this should be doable reasonably easily. In the security world, there is the notion of a "one time" password. This is sometimes referred to as a "disposable password." Normally these are restricted to the (easily typable) ASCII values. So, [a-zA-z0-9] and a bunch of easily typable symbols. like comma, period, semi colon, and parenthesis. In your case, though, you'd probably want to limit the range to [0-9] and possibly include * and #.

I am unable to explain all the technical details of how these one-time codes are generated (or work) adequately. There is some intermediate math behind it, which I'd butcher without first reviewing it myself. Suffice it to say that you use an algorithm to generate a stream of one time passwords. No matter how mnay previous codes you know, the subsequent one should be impossibel to guess! In your case, you'll simply use each password on the list as the user's random code.

Rather than fail at explaining the details of the implementation myself, I'll direct you to a 9 page article where you can read up on it youself: https://www.grc.com/ppp.htm

查看更多
Emotional °昔
7楼-- · 2019-01-21 06:32

You linked to the check digits project, and using the "encode" function seems like a good solution. It says:

encode may throw an exception if 'bad' data (e.g. non-numeric) is passed to it, while verify only returns true or false. The idea here is that encode normally gets it's data from 'trusted' internal sources (a database key for instance), so it should be pretty usual, in fact, exceptional that bad data is being passed in.

So it sounds like you could pass the encode function a database key (5 digits, for instance) and you could get a number out that would meet your requirements.

查看更多
登录 后发表回答