1 // Copyright 2013 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include "components/autofill/core/browser/validation.h"
6 
7 #include <stddef.h>
8 
9 #include "base/check.h"
10 #include "base/notreached.h"
11 #include "base/strings/string_number_conversions.h"
12 #include "base/strings/string_piece.h"
13 #include "base/strings/string_util.h"
14 #include "base/strings/utf_string_conversions.h"
15 #include "base/time/time.h"
16 #include "components/autofill/core/browser/autofill_data_util.h"
17 #include "components/autofill/core/browser/autofill_regex_constants.h"
18 #include "components/autofill/core/browser/autofill_regexes.h"
19 #include "components/autofill/core/browser/data_model/credit_card.h"
20 #include "components/autofill/core/browser/geo/phone_number_i18n.h"
21 #include "components/autofill/core/browser/geo/state_names.h"
22 #include "components/autofill/core/common/autofill_clock.h"
23 #include "components/strings/grit/components_strings.h"
24 #include "ui/base/l10n/l10n_util.h"
25 
26 namespace autofill {
27 
IsValidCreditCardExpirationDate(int year,int month,const base::Time & now)28 bool IsValidCreditCardExpirationDate(int year,
29                                      int month,
30                                      const base::Time& now) {
31   if (month < 1 || month > 12)
32     return false;
33 
34   base::Time::Exploded now_exploded;
35   now.LocalExplode(&now_exploded);
36 
37   if (year < now_exploded.year)
38     return false;
39 
40   if (year == now_exploded.year && month < now_exploded.month)
41     return false;
42 
43   return true;
44 }
45 
IsValidCreditCardExpirationYear(int year,const base::Time & now)46 bool IsValidCreditCardExpirationYear(int year, const base::Time& now) {
47   base::Time::Exploded now_exploded;
48   now.LocalExplode(&now_exploded);
49 
50   return year >= now_exploded.year;
51 }
52 
IsValidCreditCardNumber(const base::string16 & text)53 bool IsValidCreditCardNumber(const base::string16& text) {
54   const base::string16 number = CreditCard::StripSeparators(text);
55 
56   if (!HasCorrectLength(number))
57     return false;
58 
59   return PassesLuhnCheck(number);
60 }
61 
HasCorrectLength(const base::string16 & number)62 bool HasCorrectLength(const base::string16& number) {
63   // Credit card numbers are at most 19 digits in length, 12 digits seems to
64   // be a fairly safe lower-bound [1].  Specific card issuers have more rigidly
65   // defined sizes.
66   // (Last updated: May 29, 2017)
67   // [1] https://en.wikipedia.org/wiki/Payment_card_number.
68   // CardEditor.isCardNumberLengthMaxium() needs to be kept in sync.
69   const char* const type = CreditCard::GetCardNetwork(number);
70   if (type == kAmericanExpressCard && number.size() != 15)
71     return false;
72   if (type == kDinersCard && number.size() != 14)
73     return false;
74   if (type == kDiscoverCard && number.size() != 16)
75     return false;
76   if (type == kEloCard && number.size() != 16)
77     return false;
78   if (type == kJCBCard && number.size() != 16)
79     return false;
80   if (type == kMasterCard && number.size() != 16)
81     return false;
82   if (type == kMirCard && number.size() != 16)
83     return false;
84   if (type == kTroyCard && number.size() != 16)
85     return false;
86   if (type == kUnionPay && (number.size() < 16 || number.size() > 19))
87     return false;
88   if (type == kVisaCard && number.size() != 13 && number.size() != 16 &&
89       number.size() != 19)
90     return false;
91   if (type == kGenericCard && (number.size() < 12 || number.size() > 19))
92     return false;
93 
94   return true;
95 }
96 
97 // TODO(crbug.com/927767): Add unit tests for this function.
PassesLuhnCheck(const base::string16 & number)98 bool PassesLuhnCheck(const base::string16& number) {
99   // Use the Luhn formula [3] to validate the number.
100   // [3] http://en.wikipedia.org/wiki/Luhn_algorithm
101   int sum = 0;
102   bool odd = false;
103   for (base::string16::const_reverse_iterator iter = number.rbegin();
104        iter != number.rend(); ++iter) {
105     if (!base::IsAsciiDigit(*iter))
106       return false;
107 
108     int digit = *iter - '0';
109     if (odd) {
110       digit *= 2;
111       sum += digit / 10 + digit % 10;
112     } else {
113       sum += digit;
114     }
115     odd = !odd;
116   }
117 
118   return (sum % 10) == 0;
119 }
120 
IsValidCreditCardSecurityCode(const base::string16 & code,const base::StringPiece card_network)121 bool IsValidCreditCardSecurityCode(const base::string16& code,
122                                    const base::StringPiece card_network) {
123   return code.length() == GetCvcLengthForCardNetwork(card_network) &&
124          base::ContainsOnlyChars(code, base::ASCIIToUTF16("0123456789"));
125 }
126 
IsValidCreditCardNumberForBasicCardNetworks(const base::string16 & text,const std::set<std::string> & supported_basic_card_networks,base::string16 * error_message)127 bool IsValidCreditCardNumberForBasicCardNetworks(
128     const base::string16& text,
129     const std::set<std::string>& supported_basic_card_networks,
130     base::string16* error_message) {
131   DCHECK(error_message);
132 
133   // The type check is cheaper than the credit card number check.
134   const std::string basic_card_issuer_network =
135       autofill::data_util::GetPaymentRequestData(
136           CreditCard::GetCardNetwork(text))
137           .basic_card_issuer_network;
138   if (!supported_basic_card_networks.count(basic_card_issuer_network)) {
139     *error_message = l10n_util::GetStringUTF16(
140         IDS_PAYMENTS_VALIDATION_UNSUPPORTED_CREDIT_CARD_TYPE);
141     return false;
142   }
143 
144   if (IsValidCreditCardNumber(text))
145     return true;
146 
147   *error_message = l10n_util::GetStringUTF16(
148       IDS_PAYMENTS_CARD_NUMBER_INVALID_VALIDATION_MESSAGE);
149   return false;
150 }
151 
IsValidEmailAddress(const base::string16 & text)152 bool IsValidEmailAddress(const base::string16& text) {
153   // E-Mail pattern as defined by the WhatWG. (4.10.7.1.5 E-Mail state)
154   const base::string16 kEmailPattern = base::ASCIIToUTF16(
155       "^[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+@"
156       "[a-zA-Z0-9-]+(?:\\.[a-zA-Z0-9-]+)*$");
157   return MatchesPattern(text, kEmailPattern);
158 }
159 
IsValidState(const base::string16 & text)160 bool IsValidState(const base::string16& text) {
161   return !state_names::GetAbbreviationForName(text).empty() ||
162          !state_names::GetNameForAbbreviation(text).empty();
163 }
164 
IsPossiblePhoneNumber(const base::string16 & text,const std::string & country_code)165 bool IsPossiblePhoneNumber(const base::string16& text,
166                            const std::string& country_code) {
167   return i18n::IsPossiblePhoneNumber(base::UTF16ToUTF8(text), country_code);
168 }
169 
IsValidZip(const base::string16 & text)170 bool IsValidZip(const base::string16& text) {
171   const base::string16 kZipPattern = base::ASCIIToUTF16("^\\d{5}(-\\d{4})?$");
172   return MatchesPattern(text, kZipPattern);
173 }
174 
IsSSN(const base::string16 & text)175 bool IsSSN(const base::string16& text) {
176   base::string16 number_string;
177   base::RemoveChars(text, base::ASCIIToUTF16("- "), &number_string);
178 
179   // A SSN is of the form AAA-GG-SSSS (A = area number, G = group number, S =
180   // serial number). The validation we do here is simply checking if the area,
181   // group, and serial numbers are valid.
182   //
183   // Historically, the area number was assigned per state, with the group number
184   // ascending in an alternating even/odd sequence. With that scheme it was
185   // possible to check for validity by referencing a table that had the highest
186   // group number assigned for a given area number. (This was something that
187   // Chromium never did though, because the "high group" values were constantly
188   // changing.)
189   //
190   // However, starting on 25 June 2011 the SSA began issuing SSNs randomly from
191   // all areas and groups. Group numbers and serial numbers of zero remain
192   // invalid, and areas 000, 666, and 900-999 remain invalid.
193   //
194   // References for current practices:
195   //   http://www.socialsecurity.gov/employer/randomization.html
196   //   http://www.socialsecurity.gov/employer/randomizationfaqs.html
197   //
198   // References for historic practices:
199   //   http://www.socialsecurity.gov/history/ssn/geocard.html
200   //   http://www.socialsecurity.gov/employer/stateweb.htm
201   //   http://www.socialsecurity.gov/employer/ssnvhighgroup.htm
202 
203   if (number_string.length() != 9 || !base::IsStringASCII(number_string))
204     return false;
205 
206   int area;
207   if (!base::StringToInt(
208           base::StringPiece16(number_string.begin(), number_string.begin() + 3),
209           &area)) {
210     return false;
211   }
212   if (area < 1 || area == 666 || area >= 900) {
213     return false;
214   }
215 
216   int group;
217   if (!base::StringToInt(base::StringPiece16(number_string.begin() + 3,
218                                              number_string.begin() + 5),
219                          &group) ||
220       group == 0) {
221     return false;
222   }
223 
224   int serial;
225   if (!base::StringToInt(base::StringPiece16(number_string.begin() + 5,
226                                              number_string.begin() + 9),
227                          &serial) ||
228       serial == 0) {
229     return false;
230   }
231 
232   return true;
233 }
234 
IsValidForType(const base::string16 & value,ServerFieldType type,base::string16 * error_message)235 bool IsValidForType(const base::string16& value,
236                     ServerFieldType type,
237                     base::string16* error_message) {
238   switch (type) {
239     case CREDIT_CARD_NAME_FULL:
240       if (!value.empty())
241         return true;
242 
243       if (error_message) {
244         *error_message =
245             l10n_util::GetStringUTF16(IDS_PAYMENTS_VALIDATION_INVALID_NAME);
246       }
247       break;
248 
249     case CREDIT_CARD_EXP_MONTH: {
250       CreditCard temp;
251       // Expiration month was in an invalid format.
252       temp.SetExpirationMonthFromString(value, /* app_locale= */ std::string());
253       if (temp.expiration_month() == 0) {
254         if (error_message) {
255           *error_message = l10n_util::GetStringUTF16(
256               IDS_PAYMENTS_VALIDATION_INVALID_CREDIT_CARD_EXPIRATION_MONTH);
257         }
258         break;
259       }
260       return true;
261     }
262 
263     case CREDIT_CARD_EXP_2_DIGIT_YEAR:
264     case CREDIT_CARD_EXP_4_DIGIT_YEAR: {
265       CreditCard temp;
266       temp.SetExpirationYearFromString(value);
267       // Expiration year was in an invalid format.
268       if ((temp.expiration_year() == 0) ||
269           (type == CREDIT_CARD_EXP_2_DIGIT_YEAR && value.size() != 2u) ||
270           (type == CREDIT_CARD_EXP_4_DIGIT_YEAR && value.size() != 4u)) {
271         if (error_message) {
272           *error_message = l10n_util::GetStringUTF16(
273               IDS_PAYMENTS_VALIDATION_INVALID_CREDIT_CARD_EXPIRATION_YEAR);
274         }
275         break;
276       }
277 
278       base::Time::Exploded now_exploded;
279       AutofillClock::Now().LocalExplode(&now_exploded);
280       if (temp.expiration_year() >= now_exploded.year)
281         return true;
282 
283       // If the year is before this year, it's expired.
284       if (error_message) {
285         *error_message = l10n_util::GetStringUTF16(
286             IDS_PAYMENTS_VALIDATION_INVALID_CREDIT_CARD_EXPIRED);
287       }
288       break;
289     }
290 
291     case CREDIT_CARD_EXP_DATE_2_DIGIT_YEAR:
292     case CREDIT_CARD_EXP_DATE_4_DIGIT_YEAR: {
293       const base::string16 pattern =
294           type == CREDIT_CARD_EXP_DATE_2_DIGIT_YEAR
295               ? base::UTF8ToUTF16("^[0-9]{1,2}[-/|]?[0-9]{2}$")
296               : base::UTF8ToUTF16("^[0-9]{1,2}[-/|]?[0-9]{4}$");
297 
298       CreditCard temp;
299       temp.SetExpirationDateFromString(value);
300 
301       // Expiration date was in an invalid format.
302       if (temp.expiration_month() == 0 || temp.expiration_year() == 0 ||
303           !MatchesPattern(value, pattern)) {
304         if (error_message) {
305           *error_message = l10n_util::GetStringUTF16(
306               IDS_PAYMENTS_CARD_EXPIRATION_INVALID_VALIDATION_MESSAGE);
307         }
308         break;
309       }
310 
311       // Checking for card expiration.
312       if (IsValidCreditCardExpirationDate(temp.expiration_year(),
313                                           temp.expiration_month(),
314                                           AutofillClock::Now())) {
315         return true;
316       }
317 
318       if (error_message) {
319         *error_message = l10n_util::GetStringUTF16(
320             IDS_PAYMENTS_VALIDATION_INVALID_CREDIT_CARD_EXPIRED);
321       }
322       break;
323     }
324 
325     case CREDIT_CARD_NUMBER:
326       NOTREACHED() << "IsValidCreditCardNumberForBasicCardNetworks should be "
327                    << "used to validate credit card numbers";
328       break;
329 
330     default:
331       // Other types such as CREDIT_CARD_TYPE and CREDIT_CARD_VERIFICATION_CODE
332       // are not validated for now.
333       NOTREACHED() << "Attempting to validate unsupported type " << type;
334       break;
335   }
336   return false;
337 }
338 
GetCvcLengthForCardNetwork(const base::StringPiece card_network)339 size_t GetCvcLengthForCardNetwork(const base::StringPiece card_network) {
340   if (card_network == kAmericanExpressCard)
341     return AMEX_CVC_LENGTH;
342 
343   return GENERAL_CVC_LENGTH;
344 }
345 
IsUPIVirtualPaymentAddress(const base::string16 & value)346 bool IsUPIVirtualPaymentAddress(const base::string16& value) {
347   return MatchesPattern(value, base::ASCIIToUTF16(kUPIVirtualPaymentAddressRe));
348 }
349 
IsInternationalBankAccountNumber(const base::string16 & value)350 bool IsInternationalBankAccountNumber(const base::string16& value) {
351   base::string16 no_spaces;
352   base::RemoveChars(value, base::ASCIIToUTF16(" "), &no_spaces);
353   return MatchesPattern(no_spaces,
354                         base::ASCIIToUTF16(kInternationalBankAccountNumberRe));
355 }
356 
IsPlausibleCreditCardCVCNumber(const base::string16 & value)357 bool IsPlausibleCreditCardCVCNumber(const base::string16& value) {
358   return MatchesPattern(value, base::ASCIIToUTF16(kCreditCardCVCPattern));
359 }
360 
IsPlausible4DigitExpirationYear(const base::string16 & value)361 bool IsPlausible4DigitExpirationYear(const base::string16& value) {
362   return MatchesPattern(value,
363                         base::ASCIIToUTF16(kCreditCard4DigitExpYearPattern));
364 }
365 }  // namespace autofill
366