19 package org.sleuthkit.autopsy.keywordsearch;
21 import com.google.common.base.CharMatcher;
22 import com.google.common.collect.ImmutableSet;
23 import com.google.common.collect.Range;
24 import com.google.common.collect.RangeMap;
25 import com.google.common.collect.TreeRangeMap;
27 import org.apache.commons.lang3.StringUtils;
28 import org.apache.commons.validator.routines.checkdigit.LuhnCheckDigit;
43 final class CreditCardValidator {
45 private CreditCardValidator() {
48 private static final LuhnCheckDigit CREDIT_CARD_NUM_LUHN_CHECK =
new LuhnCheckDigit();
53 static private final RangeMap<Integer, Set<Integer>> allowedLengths = TreeRangeMap.create();
54 private static final ImmutableSet<Integer> Set12to19 = ImmutableSet.of(12, 13, 14, 15, 16, 17, 18, 19);
55 private static final ImmutableSet<Integer> Set14to19 = ImmutableSet.of(14, 15, 16, 17, 18, 19);
56 private static final ImmutableSet<Integer> Set16to19 = ImmutableSet.of(16, 17, 18, 29);
60 allowedLengths.put(Range.closedOpen(34000000, 35000000), ImmutableSet.of(15));
61 allowedLengths.put(Range.closedOpen(37000000, 38000000), ImmutableSet.of(15));
64 allowedLengths.put(Range.closedOpen(40000000, 50000000), Set12to19);
67 allowedLengths.put(Range.closedOpen(40260000, 40270000), ImmutableSet.of(16));
68 allowedLengths.put(Range.closedOpen(41750000, 41750100), ImmutableSet.of(16));
69 allowedLengths.put(Range.closedOpen(44050000, 44060000), ImmutableSet.of(16));
70 allowedLengths.put(Range.closedOpen(45080000, 45090000), ImmutableSet.of(16));
71 allowedLengths.put(Range.closedOpen(48440000, 48450000), ImmutableSet.of(16));
72 allowedLengths.put(Range.closedOpen(49130000, 49140000), ImmutableSet.of(16));
73 allowedLengths.put(Range.closedOpen(49170000, 49180000), ImmutableSet.of(16));
76 allowedLengths.put(Range.closedOpen(62000000, 63000000), Set16to19);
79 allowedLengths.put(Range.closedOpen(51000000, 56000000), ImmutableSet.of(16));
80 allowedLengths.put(Range.closedOpen(22210000, 27210000), ImmutableSet.of(16));
83 allowedLengths.put(Range.closedOpen(50609900, 50619900), ImmutableSet.of(16, 19));
84 allowedLengths.put(Range.closedOpen(65000200, 65002700), ImmutableSet.of(16, 19));
87 allowedLengths.put(Range.closedOpen(50000000, 50100000), Set12to19);
88 allowedLengths.put(Range.closedOpen(56000000, 59000000), Set12to19);
89 allowedLengths.put(Range.closedOpen(60000000, 70000000), Set12to19);
90 allowedLengths.put(Range.closedOpen(63900000, 63910000), Set12to19);
91 allowedLengths.put(Range.closedOpen(67000000, 68000000), Set12to19);
94 allowedLengths.put(Range.closedOpen(30000000, 30600000), Set16to19);
95 allowedLengths.put(Range.closedOpen(30950000, 30960000), Set16to19);
96 allowedLengths.put(Range.closedOpen(36000000, 37000000), Set14to19);
97 allowedLengths.put(Range.closedOpen(38000000, 40000000), Set16to19);
100 allowedLengths.put(Range.closedOpen(54000000, 56000000), Set14to19);
103 allowedLengths.put(Range.closedOpen(60110000, 60120000), Set16to19);
104 allowedLengths.put(Range.closedOpen(62212600, 62292600), Set16to19);
105 allowedLengths.put(Range.closedOpen(64400000, 66000000), Set16to19);
108 allowedLengths.put(Range.closedOpen(35280000, 35900000), Set16to19);
111 allowedLengths.put(Range.closedOpen(50190000, 50200000), Set16to19);
114 allowedLengths.put(Range.closedOpen(63600000, 63700000), Set16to19);
127 static public boolean isValidCCN(String rawCCN) {
129 boolean hasSpace = StringUtils.contains(rawCCN,
' ');
130 boolean hasDash = StringUtils.contains(rawCCN,
'-');
131 if (hasSpace && hasDash) {
135 Character separator = null;
138 }
else if (hasDash) {
142 final String cannonicalCCN;
144 if (separator != null) {
146 cannonicalCCN = CharMatcher.anyOf(separator.toString()).removeFrom(rawCCN);
147 splitCCN = rawCCN.split(separator.toString());
150 cannonicalCCN = rawCCN;
151 splitCCN =
new String[]{cannonicalCCN};
154 if (
false == lengthMatchesBin(cannonicalCCN)) {
159 switch (cannonicalCCN.length()) {
161 if (
false == isValid15DigitGrouping(splitCCN)) {
166 if (
false == isValid16DigitGrouping(splitCCN)) {
171 if (
false == isValid19DigitGrouping(splitCCN)) {
176 if (
false == isValidOtherDigitGrouping(splitCCN)) {
181 return CREDIT_CARD_NUM_LUHN_CHECK.isValid(cannonicalCCN);
184 static private boolean lengthMatchesBin(String cannonicalCCN) {
185 String BIN = cannonicalCCN.substring(0, 8);
186 final Set<Integer> lengthsForBIN = allowedLengths.get(Integer.valueOf(BIN));
187 return null == lengthsForBIN || lengthsForBIN.contains(cannonicalCCN.length());
190 static private boolean isValidOtherDigitGrouping(String[] splitCCN) {
191 if (splitCCN.length == 1) {
194 return splitCCN[0].length() == 4;
198 static private boolean isValid19DigitGrouping(String[] splitCCN) {
199 switch (splitCCN.length) {
203 return splitCCN[0].length() == 6
204 && splitCCN[1].length() == 13;
206 return splitCCN[0].length() == 4
207 && splitCCN[1].length() == 4
208 && splitCCN[2].length() == 4
209 && splitCCN[3].length() == 4
210 && splitCCN[4].length() == 3;
216 static private boolean isValid16DigitGrouping(String[] splitCCN) {
217 switch (splitCCN.length) {
221 return splitCCN[0].length() == 4
222 && splitCCN[1].length() == 4
223 && splitCCN[2].length() == 4
224 && splitCCN[3].length() == 4;
230 static private boolean isValid15DigitGrouping(String[] splitCCN) {
231 switch (splitCCN.length) {
235 return (splitCCN[0].length() == 4 && splitCCN[1].length() == 6 && splitCCN[2].length() == 5);