Created
August 8, 2012 17:20
-
-
Save bryanhunter/3296790 to your computer and use it in GitHub Desktop.
Testing idea of using sum of card numbers as security question
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Based on Twitter conversation with @b6n | |
*Question 1* | |
The digits in the credit card pattern 4811 XXXX XXXX 2632 sum to 77, what are the | |
middle 8?There are 112,400 card numbers (about 1.2%) in this range that 1) pass | |
the luhn check and 2) sum to 77. The credit card isn't in great danger. | |
*Question 2*Does the sum of the card digits make a good security question for | |
phone support?The first hurdle is having a customer add the 16 digits without | |
miss-adding or freaking out. Next, the numbers do cluster to the middle. A bad | |
guy who knew the card met the pattern 4811 XXXX XXXX 2632 could create a | |
histogram and have a 4.8% chance of guessing the sum. In your case where 77 is | |
the sum you would be pretty safe. If given three attempts before alarm bells, | |
only a bad gambler would go outside of 61, 62, 63. Of course, if you were one of | |
14.4% of the population with a card sum falling in that range (61,62,63) you | |
wouldn't be too happy with this scheme. You would become an albino rabbit. | |
Sum Freq Prob | |
29 16 0.000% | |
30 20 0.000% | |
33 16 0.000% | |
34 312 0.003% | |
35 1040 0.010% | |
36 1960 0.020% | |
37 2400 0.024% | |
38 2496 0.025% | |
39 3680 0.037% | |
40 6740 0.067% | |
41 11200 0.112% | |
42 16804 0.168% | |
43 23568 0.236% | |
44 32248 0.322% | |
45 42960 0.430% | |
46 57040 0.570% | |
47 73888 0.739% | |
48 96128 0.961% | |
49 119344 1.193% | |
50 142040 1.420% | |
51 164960 1.650% | |
52 197300 1.973% | |
53 233856 2.339% | |
54 270104 2.701% | |
55 304800 3.048% | |
56 343220 3.432% | |
57 375472 3.755% | |
58 403340 4.033% | |
59 430848 4.308% | |
60 459520 4.595% | |
61 474320 4.743% | |
62 481156 4.812% <- Best guess | |
63 478464 4.785% | |
64 472500 4.725% | |
65 460000 4.600% | |
66 449820 4.498% | |
67 430016 4.300% | |
68 404452 4.045% | |
69 372992 3.730% | |
70 341740 3.417% | |
71 305760 3.058% | |
72 272732 2.727% | |
73 241152 2.412% | |
74 207876 2.079% | |
75 170960 1.710% | |
76 139140 1.391% | |
77 112400 1.124% <- Not in the running | |
78 90484 0.905% | |
79 72000 0.720% | |
80 57580 0.576% | |
81 43280 0.433% | |
82 31448 0.314% | |
83 22544 0.225% | |
84 16820 0.168% | |
85 12240 0.122% | |
86 8820 0.088% | |
87 5824 0.058% | |
88 3100 0.031% | |
89 1120 0.011% | |
90 360 0.004% | |
91 480 0.005% | |
92 560 0.006% | |
93 400 0.004% | |
94 140 0.001% |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
-module(luhnfun). | |
-export([find_matching_cards/0, calc_best_guesses_for_sum/0]). | |
% Slapped together to test credit cards in the range 4811xxxxxxxx2632 | |
% -- public | |
find_matching_cards() -> | |
{ok, IoDevice} = file:open("cards.txt", write), | |
Logger = fun(Message) -> io:format(IoDevice, "~s\r\n", [Message]) end, | |
[test_card(X, Logger) || X <- lists:seq(0, 99999999)], | |
file:close(IoDevice). | |
calc_best_guesses_for_sum() -> | |
D1 = orddict:new(), | |
D2 = lists:foldl(fun(Middle8, Dict) -> tally_hits(Dict, Middle8) end, | |
D1, lists:seq(0,99999999)), | |
List = orddict:to_list(D2), | |
[io:format("Sum: ~p, Frequency: ~p ~n", [Key, Value]) || {Key, Value} <- List ]. | |
% -- private | |
test_card(Middle8, Logger) -> | |
CardNumber = lists:flatten(io_lib:format("4811~8..0B2632", [Middle8])), | |
case validate(CardNumber) of | |
true -> | |
Logger(CardNumber), | |
hit; | |
_ -> miss | |
end. | |
tally_hits(Dict, Middle8) -> | |
CardNumber = lists:flatten(io_lib:format("4811~8..0B2632", [Middle8])), | |
case is_luhn(CardNumber) of | |
true -> | |
Key = lists:sum([X-48 || X <- CardNumber]), | |
orddict:update_counter(Key, 1, Dict); | |
_ -> | |
Dict | |
end. | |
validate(CardNumber) -> | |
sum_check(CardNumber, 77) andalso is_luhn(CardNumber). | |
is_luhn(CardNumber) -> | |
[Checksum|Numbers] = [X -48 || X <- lists:reverse(CardNumber)], | |
Sum = luhn_sum(Numbers, 0, false), | |
(Sum + Checksum) rem 10 == 0. | |
luhn_sum([], RunningSum, _) -> | |
RunningSum; | |
luhn_sum([H|T], RunningSum, IsEven) when IsEven == true -> | |
luhn_sum(T, RunningSum + H, not IsEven); | |
luhn_sum([H|T], RunningSum, IsEven) -> | |
Add = case H < 5 of | |
true -> H * 2; | |
false -> H * 2 -9 | |
end, | |
luhn_sum(T, RunningSum + Add, not IsEven). | |
sum_check(CardNumber, MagicSum) -> | |
MagicSum == lists:sum([X-48 || X <- CardNumber]). |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment