96 lines
4.7 KiB
Java
96 lines
4.7 KiB
Java
package ru.pdguard.mask;
|
|
|
|
import ru.pdguard.detect.RuleRegistry;
|
|
|
|
/**
|
|
* Правдоподобные подставные значения вместо настоящих.
|
|
*
|
|
* <p>Модель получает текст, который выглядит естественно, и качество ответа
|
|
* страдает меньше, чем от звёздочек. Значения детерминированы: одно и то же
|
|
* исходное значение всегда даёт одну и ту же подстановку.
|
|
*/
|
|
final class Synthetic {
|
|
|
|
private static final String[] SURNAMES =
|
|
{"Лаврентьев", "Мещеряков", "Тихомиров", "Ясенев", "Бурмистров", "Кольцов"};
|
|
private static final String[] NAMES = {"Артём", "Никита", "Глеб", "Тимур", "Марк", "Лев"};
|
|
private static final String[] PATRONYMICS =
|
|
{"Артёмович", "Никитич", "Глебович", "Тимурович", "Маркович", "Львович"};
|
|
private static final String[] DOMAINS = {"example.com", "example.org", "example.net"};
|
|
|
|
private Synthetic() {
|
|
}
|
|
|
|
static String forType(String type, String value, int ordinal) {
|
|
int seed = Math.abs(value.hashCode());
|
|
return switch (type) {
|
|
case RuleRegistry.FIO -> pick(SURNAMES, seed) + " " + pick(NAMES, seed >> 3)
|
|
+ " " + pick(PATRONYMICS, seed >> 6);
|
|
case RuleRegistry.CARDHOLDER -> "IVAN PETROV";
|
|
case RuleRegistry.EMAIL -> "user" + ordinal + "@" + pick(DOMAINS, seed);
|
|
case RuleRegistry.PHONE -> "+7 9" + digits(seed, 2) + " " + digits(seed >> 4, 3)
|
|
+ "-" + digits(seed >> 8, 2) + "-" + digits(seed >> 12, 2);
|
|
case RuleRegistry.CARD -> luhnCard(seed);
|
|
case RuleRegistry.PASSPORT, RuleRegistry.DRIVER_LICENSE, RuleRegistry.FOREIGN_PASSPORT,
|
|
RuleRegistry.MILITARY_ID -> digits(seed, 4) + " " + digits(seed >> 6, 6);
|
|
case RuleRegistry.INN -> digits(seed, 12);
|
|
case RuleRegistry.MEDICAL_POLICY -> digits(seed, 16);
|
|
case RuleRegistry.SNILS -> digits(seed, 3) + "-" + digits(seed >> 4, 3)
|
|
+ "-" + digits(seed >> 8, 3) + " " + digits(seed >> 12, 2);
|
|
case RuleRegistry.BIRTH_DATE, RuleRegistry.PASSPORT_DATE, RuleRegistry.DATE -> syntheticDate(seed);
|
|
case RuleRegistry.ADDRESS_CITY -> "Зареченск";
|
|
case RuleRegistry.ADDRESS_STREET -> "Сосновая";
|
|
case RuleRegistry.ADDRESS_HOUSE -> String.valueOf(1 + Math.floorMod(seed, 90));
|
|
case RuleRegistry.ADDRESS_FLAT -> String.valueOf(1 + Math.floorMod(seed, 200));
|
|
case RuleRegistry.ADDRESS_POSTCODE -> digits(seed, 6);
|
|
case RuleRegistry.ADDRESS_COUNTRY -> "Заречье";
|
|
case RuleRegistry.CVV -> digits(seed, 3);
|
|
case RuleRegistry.PIN -> digits(seed, 4);
|
|
// Для остальных типов правдоподобной замены нет — отдаём токен.
|
|
default -> "[" + type + "_" + ordinal + "]";
|
|
};
|
|
}
|
|
|
|
private static String pick(String[] options, int seed) {
|
|
return options[Math.floorMod(seed, options.length)];
|
|
}
|
|
|
|
private static String syntheticDate(int seed) {
|
|
int day = 1 + Math.floorMod(seed, 28);
|
|
int month = 1 + Math.floorMod(seed >> 5, 12);
|
|
int year = 1960 + Math.floorMod(seed >> 9, 45);
|
|
return String.format("%02d.%02d.%d", day, month, year);
|
|
}
|
|
|
|
private static String digits(int seed, int count) {
|
|
StringBuilder sb = new StringBuilder(count);
|
|
int value = Math.abs(seed);
|
|
for (int i = 0; i < count; i++) {
|
|
sb.append((char) ('0' + Math.floorMod(value, 10)));
|
|
value = value / 10 + (i + 1) * 7;
|
|
}
|
|
return sb.toString();
|
|
}
|
|
|
|
/** Номер карты, проходящий проверку алгоритмом Луна: подстановка должна выглядеть настоящей. */
|
|
private static String luhnCard(int seed) {
|
|
StringBuilder body = new StringBuilder("4").append(digits(seed, 14));
|
|
int sum = 0;
|
|
boolean doubled = true;
|
|
for (int i = body.length() - 1; i >= 0; i--) {
|
|
int d = body.charAt(i) - '0';
|
|
if (doubled) {
|
|
d *= 2;
|
|
if (d > 9) {
|
|
d -= 9;
|
|
}
|
|
}
|
|
sum += d;
|
|
doubled = !doubled;
|
|
}
|
|
body.append((10 - sum % 10) % 10);
|
|
return body.substring(0, 4) + " " + body.substring(4, 8) + " "
|
|
+ body.substring(8, 12) + " " + body.substring(12);
|
|
}
|
|
}
|