| // Copyright 2013 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "components/autofill/core/browser/validation.h" |
| |
| #include <stddef.h> |
| |
| #include "base/logging.h" |
| #include "base/strings/string_number_conversions.h" |
| #include "base/strings/string_piece.h" |
| #include "base/strings/string_util.h" |
| #include "base/strings/utf_string_conversions.h" |
| #include "base/time/time.h" |
| #include "components/autofill/core/browser/autofill_data_util.h" |
| #include "components/autofill/core/browser/credit_card.h" |
| #include "components/autofill/core/browser/personal_data_manager.h" |
| #include "components/autofill/core/browser/phone_number_i18n.h" |
| #include "components/autofill/core/browser/state_names.h" |
| #include "components/autofill/core/common/autofill_clock.h" |
| #include "components/autofill/core/common/autofill_regex_constants.h" |
| #include "components/autofill/core/common/autofill_regexes.h" |
| #include "components/strings/grit/components_strings.h" |
| #include "ui/base/l10n/l10n_util.h" |
| |
| namespace autofill { |
| |
| bool IsValidCreditCardExpirationDate(int year, |
| int month, |
| const base::Time& now) { |
| if (month < 1 || month > 12) |
| return false; |
| |
| base::Time::Exploded now_exploded; |
| now.LocalExplode(&now_exploded); |
| |
| if (year < now_exploded.year) |
| return false; |
| |
| if (year == now_exploded.year && month < now_exploded.month) |
| return false; |
| |
| return true; |
| } |
| |
| bool IsValidCreditCardExpirationYear(int year, const base::Time& now) { |
| base::Time::Exploded now_exploded; |
| now.LocalExplode(&now_exploded); |
| |
| return year >= now_exploded.year; |
| } |
| |
| bool IsValidCreditCardNumber(const base::string16& text) { |
| base::string16 number = CreditCard::StripSeparators(text); |
| |
| if (!HasCorrectLength(number)) |
| return false; |
| |
| return PassesLuhnCheck(number); |
| } |
| |
| bool HasCorrectLength(const base::string16& number) { |
| // Credit card numbers are at most 19 digits in length, 12 digits seems to |
| // be a fairly safe lower-bound [1]. Specific card issuers have more rigidly |
| // defined sizes. |
| // (Last updated: May 29, 2017) |
| // [1] https://en.wikipedia.org/wiki/Payment_card_number. |
| // CardEditor.isCardNumberLengthMaxium() needs to be kept in sync. |
| const char* const type = CreditCard::GetCardNetwork(number); |
| if (type == kAmericanExpressCard && number.size() != 15) |
| return false; |
| if (type == kDinersCard && number.size() != 14) |
| return false; |
| if (type == kDiscoverCard && number.size() != 16) |
| return false; |
| if (type == kEloCard && number.size() != 16) |
| return false; |
| if (type == kJCBCard && number.size() != 16) |
| return false; |
| if (type == kMasterCard && number.size() != 16) |
| return false; |
| if (type == kMirCard && number.size() != 16) |
| return false; |
| if (type == kUnionPay && (number.size() < 16 || number.size() > 19)) |
| return false; |
| if (type == kVisaCard && number.size() != 13 && number.size() != 16 && |
| number.size() != 19) |
| return false; |
| if (type == kGenericCard && (number.size() < 12 || number.size() > 19)) |
| return false; |
| |
| return true; |
| } |
| |
| bool PassesLuhnCheck(base::string16& number) { |
| // Use the Luhn formula [3] to validate the number. |
| // [3] http://en.wikipedia.org/wiki/Luhn_algorithm |
| int sum = 0; |
| bool odd = false; |
| for (base::string16::reverse_iterator iter = number.rbegin(); |
| iter != number.rend(); ++iter) { |
| if (!base::IsAsciiDigit(*iter)) |
| return false; |
| |
| int digit = *iter - '0'; |
| if (odd) { |
| digit *= 2; |
| sum += digit / 10 + digit % 10; |
| } else { |
| sum += digit; |
| } |
| odd = !odd; |
| } |
| |
| return (sum % 10) == 0; |
| } |
| |
| bool IsValidCreditCardSecurityCode(const base::string16& code, |
| const base::StringPiece card_type) { |
| return code.length() == GetCvcLengthForCardType(card_type) && |
| base::ContainsOnlyChars(code, base::ASCIIToUTF16("0123456789")); |
| } |
| |
| bool IsValidCreditCardNumberForBasicCardNetworks( |
| const base::string16& text, |
| const std::set<std::string>& supported_basic_card_networks, |
| base::string16* error_message) { |
| DCHECK(error_message); |
| |
| // The type check is cheaper than the credit card number check. |
| const std::string basic_card_issuer_network = |
| autofill::data_util::GetPaymentRequestData( |
| CreditCard::GetCardNetwork(text)) |
| .basic_card_issuer_network; |
| if (!supported_basic_card_networks.count(basic_card_issuer_network)) { |
| *error_message = l10n_util::GetStringUTF16( |
| IDS_PAYMENTS_VALIDATION_UNSUPPORTED_CREDIT_CARD_TYPE); |
| return false; |
| } |
| |
| if (IsValidCreditCardNumber(text)) |
| return true; |
| |
| *error_message = l10n_util::GetStringUTF16( |
| IDS_PAYMENTS_CARD_NUMBER_INVALID_VALIDATION_MESSAGE); |
| return false; |
| } |
| |
| CreditCardCompletionStatus GetCompletionStatusForCard( |
| const CreditCard& card, |
| const std::string& app_locale, |
| const std::vector<AutofillProfile*> billing_addresses) { |
| CreditCardCompletionStatus status = CREDIT_CARD_COMPLETE; |
| if (card.IsExpired(autofill::AutofillClock::Now())) |
| status |= CREDIT_CARD_EXPIRED; |
| |
| if (card.number().empty()) |
| status |= CREDIT_CARD_NO_NUMBER; |
| |
| if (card.GetInfo(autofill::AutofillType(autofill::CREDIT_CARD_NAME_FULL), |
| app_locale) |
| .empty()) { |
| status |= CREDIT_CARD_NO_CARDHOLDER; |
| } |
| |
| if (card.billing_address_id().empty() || |
| !autofill::PersonalDataManager::GetProfileFromProfilesByGUID( |
| card.billing_address_id(), billing_addresses)) { |
| status |= CREDIT_CARD_NO_BILLING_ADDRESS; |
| } |
| |
| return status; |
| } |
| |
| base::string16 GetCompletionMessageForCard(CreditCardCompletionStatus status) { |
| switch (status) { |
| // No message is shown for complete or expired card (which will be fixable) |
| // in the CVC screen. |
| case CREDIT_CARD_COMPLETE: |
| case CREDIT_CARD_EXPIRED: |
| return base::string16(); |
| case CREDIT_CARD_NO_CARDHOLDER: |
| return l10n_util::GetStringUTF16(IDS_PAYMENTS_NAME_ON_CARD_REQUIRED); |
| case CREDIT_CARD_NO_NUMBER: |
| return l10n_util::GetStringUTF16( |
| IDS_PAYMENTS_CARD_NUMBER_INVALID_VALIDATION_MESSAGE); |
| case CREDIT_CARD_NO_BILLING_ADDRESS: |
| return l10n_util::GetStringUTF16( |
| IDS_PAYMENTS_CARD_BILLING_ADDRESS_REQUIRED); |
| default: |
| // Multiple things are missing |
| return l10n_util::GetStringUTF16(IDS_PAYMENTS_MORE_INFORMATION_REQUIRED); |
| } |
| } |
| |
| base::string16 GetEditDialogTitleForCard(CreditCardCompletionStatus status) { |
| switch (status) { |
| case CREDIT_CARD_COMPLETE: |
| case CREDIT_CARD_EXPIRED: |
| return l10n_util::GetStringUTF16(IDS_PAYMENTS_EDIT_CARD); |
| case CREDIT_CARD_NO_CARDHOLDER: |
| return l10n_util::GetStringUTF16(IDS_PAYMENTS_ADD_NAME_ON_CARD); |
| case CREDIT_CARD_NO_NUMBER: |
| return l10n_util::GetStringUTF16(IDS_PAYMENTS_ADD_VALID_CARD_NUMBER); |
| case CREDIT_CARD_NO_BILLING_ADDRESS: |
| return l10n_util::GetStringUTF16(IDS_PAYMENTS_ADD_BILLING_ADDRESS); |
| default: |
| // Multiple things are missing |
| return l10n_util::GetStringUTF16(IDS_PAYMENTS_ADD_MORE_INFORMATION); |
| } |
| } |
| |
| bool IsValidEmailAddress(const base::string16& text) { |
| // E-Mail pattern as defined by the WhatWG. (4.10.7.1.5 E-Mail state) |
| const base::string16 kEmailPattern = base::ASCIIToUTF16( |
| "^[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+@" |
| "[a-zA-Z0-9-]+(?:\\.[a-zA-Z0-9-]+)*$"); |
| return MatchesPattern(text, kEmailPattern); |
| } |
| |
| bool IsValidState(const base::string16& text) { |
| return !state_names::GetAbbreviationForName(text).empty() || |
| !state_names::GetNameForAbbreviation(text).empty(); |
| } |
| |
| bool IsPossiblePhoneNumber(const base::string16& text, |
| const std::string& country_code) { |
| return i18n::IsPossiblePhoneNumber(base::UTF16ToUTF8(text), country_code); |
| } |
| |
| bool IsValidZip(const base::string16& text) { |
| const base::string16 kZipPattern = base::ASCIIToUTF16("^\\d{5}(-\\d{4})?$"); |
| return MatchesPattern(text, kZipPattern); |
| } |
| |
| bool IsSSN(const base::string16& text) { |
| base::string16 number_string; |
| base::RemoveChars(text, base::ASCIIToUTF16("- "), &number_string); |
| |
| // A SSN is of the form AAA-GG-SSSS (A = area number, G = group number, S = |
| // serial number). The validation we do here is simply checking if the area, |
| // group, and serial numbers are valid. |
| // |
| // Historically, the area number was assigned per state, with the group number |
| // ascending in an alternating even/odd sequence. With that scheme it was |
| // possible to check for validity by referencing a table that had the highest |
| // group number assigned for a given area number. (This was something that |
| // Chromium never did though, because the "high group" values were constantly |
| // changing.) |
| // |
| // However, starting on 25 June 2011 the SSA began issuing SSNs randomly from |
| // all areas and groups. Group numbers and serial numbers of zero remain |
| // invalid, and areas 000, 666, and 900-999 remain invalid. |
| // |
| // References for current practices: |
| // http://www.socialsecurity.gov/employer/randomization.html |
| // http://www.socialsecurity.gov/employer/randomizationfaqs.html |
| // |
| // References for historic practices: |
| // http://www.socialsecurity.gov/history/ssn/geocard.html |
| // http://www.socialsecurity.gov/employer/stateweb.htm |
| // http://www.socialsecurity.gov/employer/ssnvhighgroup.htm |
| |
| if (number_string.length() != 9 || !base::IsStringASCII(number_string)) |
| return false; |
| |
| int area; |
| if (!base::StringToInt( |
| base::StringPiece16(number_string.begin(), number_string.begin() + 3), |
| &area)) { |
| return false; |
| } |
| if (area < 1 || area == 666 || area >= 900) { |
| return false; |
| } |
| |
| int group; |
| if (!base::StringToInt(base::StringPiece16(number_string.begin() + 3, |
| number_string.begin() + 5), |
| &group) || |
| group == 0) { |
| return false; |
| } |
| |
| int serial; |
| if (!base::StringToInt(base::StringPiece16(number_string.begin() + 5, |
| number_string.begin() + 9), |
| &serial) || |
| serial == 0) { |
| return false; |
| } |
| |
| return true; |
| } |
| |
| bool IsValidForType(const base::string16& value, |
| ServerFieldType type, |
| base::string16* error_message) { |
| switch (type) { |
| case CREDIT_CARD_NAME_FULL: |
| if (!value.empty()) |
| return true; |
| |
| if (error_message) { |
| *error_message = |
| l10n_util::GetStringUTF16(IDS_PAYMENTS_VALIDATION_INVALID_NAME); |
| } |
| break; |
| |
| case CREDIT_CARD_EXP_MONTH: { |
| CreditCard temp; |
| // Expiration month was in an invalid format. |
| temp.SetExpirationMonthFromString(value, /* app_locale= */ std::string()); |
| if (temp.expiration_month() == 0) { |
| if (error_message) { |
| *error_message = l10n_util::GetStringUTF16( |
| IDS_PAYMENTS_VALIDATION_INVALID_CREDIT_CARD_EXPIRATION_MONTH); |
| } |
| break; |
| } |
| return true; |
| } |
| |
| case CREDIT_CARD_EXP_2_DIGIT_YEAR: |
| case CREDIT_CARD_EXP_4_DIGIT_YEAR: { |
| CreditCard temp; |
| temp.SetExpirationYearFromString(value); |
| // Expiration year was in an invalid format. |
| if ((temp.expiration_year() == 0) || |
| (type == CREDIT_CARD_EXP_2_DIGIT_YEAR && value.size() != 2u) || |
| (type == CREDIT_CARD_EXP_4_DIGIT_YEAR && value.size() != 4u)) { |
| if (error_message) { |
| *error_message = l10n_util::GetStringUTF16( |
| IDS_PAYMENTS_VALIDATION_INVALID_CREDIT_CARD_EXPIRATION_YEAR); |
| } |
| break; |
| } |
| |
| base::Time::Exploded now_exploded; |
| AutofillClock::Now().LocalExplode(&now_exploded); |
| if (temp.expiration_year() >= now_exploded.year) |
| return true; |
| |
| // If the year is before this year, it's expired. |
| if (error_message) { |
| *error_message = l10n_util::GetStringUTF16( |
| IDS_PAYMENTS_VALIDATION_INVALID_CREDIT_CARD_EXPIRED); |
| } |
| break; |
| } |
| |
| case CREDIT_CARD_EXP_DATE_2_DIGIT_YEAR: |
| case CREDIT_CARD_EXP_DATE_4_DIGIT_YEAR: { |
| const base::string16 pattern = |
| type == CREDIT_CARD_EXP_DATE_2_DIGIT_YEAR |
| ? base::UTF8ToUTF16("^[0-9]{1,2}[-/|]?[0-9]{2}$") |
| : base::UTF8ToUTF16("^[0-9]{1,2}[-/|]?[0-9]{4}$"); |
| |
| CreditCard temp; |
| temp.SetExpirationDateFromString(value); |
| |
| // Expiration date was in an invalid format. |
| if (temp.expiration_month() == 0 || temp.expiration_year() == 0 || |
| !MatchesPattern(value, pattern)) { |
| if (error_message) { |
| *error_message = l10n_util::GetStringUTF16( |
| IDS_PAYMENTS_CARD_EXPIRATION_INVALID_VALIDATION_MESSAGE); |
| } |
| break; |
| } |
| |
| // Checking for card expiration. |
| if (IsValidCreditCardExpirationDate(temp.expiration_year(), |
| temp.expiration_month(), |
| AutofillClock::Now())) { |
| return true; |
| } |
| |
| if (error_message) { |
| *error_message = l10n_util::GetStringUTF16( |
| IDS_PAYMENTS_VALIDATION_INVALID_CREDIT_CARD_EXPIRED); |
| } |
| break; |
| } |
| |
| case CREDIT_CARD_NUMBER: |
| NOTREACHED() << "IsValidCreditCardNumberForBasicCardNetworks should be " |
| << "used to validate credit card numbers"; |
| break; |
| |
| default: |
| // Other types such as CREDIT_CARD_TYPE and CREDIT_CARD_VERIFICATION_CODE |
| // are not validated for now. |
| NOTREACHED() << "Attempting to validate unsupported type " << type; |
| break; |
| } |
| return false; |
| } |
| |
| size_t GetCvcLengthForCardType(const base::StringPiece card_type) { |
| if (card_type == kAmericanExpressCard) |
| return AMEX_CVC_LENGTH; |
| |
| return GENERAL_CVC_LENGTH; |
| } |
| |
| bool IsUPIVirtualPaymentAddress(const base::string16& value) { |
| return MatchesPattern(value, base::ASCIIToUTF16(kUPIVirtualPaymentAddressRe)); |
| } |
| |
| } // namespace autofill |