blob: 853d0a5c761ffbe0dd7bcde5cf3d69fc13851783 [file] [log] [blame] [edit]
// Copyright 2023 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// This is a copy of //components/autofill/core/browser/validation.cc ~2023. It
// should be used only by //components/feedback/redaction_tool/.
// We need a copy because the //components/feedback/redaction_tool source code
// is shared into ChromeOS and needs to have no dependencies outside of //base/.
// TODO(b/281812289) Deduplicate the logic and let the autofill component
// depend on this one.
#include "redaction_tool/validation.h"
#include <string_view>
#include "base/containers/adapters.h"
#include "base/containers/contains.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/string_util.h"
namespace {
enum class CreditCardIssuer {
kAmericanExpressCard,
kDinersCard,
kDiscoverCard,
kGenericCard,
kJCBCard,
kMasterCard,
kMirCard,
kTroyCard,
kUnionPay,
kVisaCard,
// Elo card is missing here as it's gated behind a feature flag. Once the card
// is GA this file should be updated here as well.
};
CreditCardIssuer GetCardNetwork(const std::string& number) {
// Credit card number specifications taken from:
// https://en.wikipedia.org/wiki/Payment_card_number,
// http://www.regular-expressions.info/creditcard.html,
// https://developer.ean.com/general-info/valid-card-types,
// http://www.bincodes.com/, and
// http://www.fraudpractice.com/FL-binCC.html.
// (Last updated: March 2021; change Troy bin range)
//
// Card Type Prefix(es) Length
// --------------------------------------------------------------------------
// Visa 4 13,16,19
// American Express 34,37 15
// Diners Club 300-305,309,36,38-39 14
// Discover Card 6011,644-649,65 16
// JCB 3528-3589 16
// Mastercard 2221-2720, 51-55 16
// MIR 2200-2204 16
// Troy 22050-22052, 9792 16
// UnionPay 62 16-19
// Check for prefixes of length 6.
if (number.size() >= 6) {
int first_six_digits = 0;
if (!base::StringToInt(number.substr(0, 6), &first_six_digits)) {
return CreditCardIssuer::kGenericCard;
}
}
// Check for prefixes of length 5.
if (number.size() >= 5) {
int first_five_digits = 0;
if (!base::StringToInt(number.substr(0, 5), &first_five_digits)) {
return CreditCardIssuer::kGenericCard;
}
if (first_five_digits == 22050 || first_five_digits == 22051 ||
first_five_digits == 22052) {
return CreditCardIssuer::kTroyCard;
}
}
// Check for prefixes of length 4.
if (number.size() >= 4) {
int first_four_digits = 0;
if (!base::StringToInt(number.substr(0, 4), &first_four_digits)) {
return CreditCardIssuer::kGenericCard;
}
if (first_four_digits >= 2200 && first_four_digits <= 2204) {
return CreditCardIssuer::kMirCard;
}
if (first_four_digits == 9792) {
return CreditCardIssuer::kTroyCard;
}
if (first_four_digits >= 2221 && first_four_digits <= 2720) {
return CreditCardIssuer::kMasterCard;
}
if (first_four_digits >= 3528 && first_four_digits <= 3589) {
return CreditCardIssuer::kJCBCard;
}
if (first_four_digits == 6011) {
return CreditCardIssuer::kDiscoverCard;
}
}
// Check for prefixes of length 3.
if (number.size() >= 3) {
int first_three_digits = 0;
if (!base::StringToInt(number.substr(0, 3), &first_three_digits)) {
return CreditCardIssuer::kGenericCard;
}
if ((first_three_digits >= 300 && first_three_digits <= 305) ||
first_three_digits == 309) {
return CreditCardIssuer::kDinersCard;
}
if (first_three_digits >= 644 && first_three_digits <= 649) {
return CreditCardIssuer::kDiscoverCard;
}
}
// Check for prefixes of length 2.
if (number.size() >= 2) {
int first_two_digits = 0;
if (!base::StringToInt(number.substr(0, 2), &first_two_digits)) {
return CreditCardIssuer::kGenericCard;
}
if (first_two_digits == 34 || first_two_digits == 37) {
return CreditCardIssuer::kAmericanExpressCard;
}
if (first_two_digits == 36 || first_two_digits == 38 ||
first_two_digits == 39) {
return CreditCardIssuer::kDinersCard;
}
if (first_two_digits >= 51 && first_two_digits <= 55) {
return CreditCardIssuer::kMasterCard;
}
if (first_two_digits == 62) {
return CreditCardIssuer::kUnionPay;
}
if (first_two_digits == 65) {
return CreditCardIssuer::kDiscoverCard;
}
}
// Check for prefixes of length 1.
if (number.empty()) {
return CreditCardIssuer::kGenericCard;
}
if (number[0] == '4') {
return CreditCardIssuer::kVisaCard;
}
return CreditCardIssuer::kGenericCard;
}
bool HasCorrectLength(const std::string& number) {
using enum CreditCardIssuer;
// 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.
if (number.size() < 12 || number.size() > 19) {
return false;
}
const std::vector<char> kUnlikelyIin{'0', '7', '8', '9'};
if (base::Contains(kUnlikelyIin, number.front())) {
return false;
}
const CreditCardIssuer type = GetCardNetwork(number);
if (type == kGenericCard) {
return true;
}
switch (number.size()) {
case 13:
return type == kVisaCard;
case 14:
return type == kDinersCard;
case 15:
return type == kAmericanExpressCard;
case 16:
return (type == kDiscoverCard || type == kJCBCard ||
type == kMasterCard || type == kMirCard || type == kTroyCard ||
type == kUnionPay || type == kVisaCard);
case 17:
[[fallthrough]];
case 18:
return type == kUnionPay;
case 19:
return (type == kUnionPay || type == kVisaCard);
default: {
return false;
}
}
}
bool PassesLuhnCheck(const std::string& 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 (const char c : base::Reversed(number)) {
if (!base::IsAsciiDigit(c)) {
return false;
}
int digit = c - '0';
if (odd) {
digit *= 2;
sum += digit / 10 + digit % 10;
} else {
sum += digit;
}
odd = !odd;
}
return (sum % 10) == 0;
}
} // namespace
namespace redaction {
bool IsValidCreditCardNumber(const std::string& number) {
if (!HasCorrectLength(number)) {
return false;
}
return PassesLuhnCheck(number);
}
} // namespace redaction