Сижу я за пк, захожу уже не помню зачем на гитхаб и вижу сообщение, что мне нужно подключить 2Fa, который будет выдавать мне коды по времени (TOTP):

Те кто не знают, это одноразовый генератор кодов каждое n (обычно 30) секунд. Бесплатного аутентификатора на пк я не нашел, честно, мне лень скачивать аутентификатор на телефон и каждый раз логиниться через него, тем более у меня будет опыт если я сам сделаю менеджер TOTP (Time-based One Time Password)
Сканирование QR-кода
Скорее всего вам будет выдан QR код, чтобы узнать код его можно заскринить и отдать QR сканеру, но мы хотим автоматизации поэтому пишем следующий код на библиотеках OpenCV и libqrencode:
QRCodeDetector detector = QRCodeDetector::QRCodeDetector();
Mat points, rectImage;
try {
string result = detector.detectAndDecode(image, points, rectImage);
if (result.empty())
cerr << "Can't scan qr code :(" << endl;
else
return result;
} catch (...) {
cerr << "Invalid error, can't read qr code :(" << endl;
}
return "";
Проверяем и получаем результат:

Но далее я столкнулся с проблемой, что QRDetector не распознает qr коды прям от начала фотографии и просто кидает ексепшн. Для этого нам необходимо расширить наше фото на несколько пикселей, я взял 25:
if (!detector.detect(image, points)) {
int indent = 25;
int newSize = image.rows + indent * 2;
Mat newImage(newSize, newSize, image.type(), Scalar(255, 255, 255));
image.copyTo(newImage(Rect(indent, indent, image.cols, image.rows)));
image = newImage;
}
И теперь фото расширится и QR-код будет распознаваться верно
Создание TOTP кодов
Далее переходим непосредственно к TOTP менеджеру. В результате чтения qr-кода мы получили строку содержащую секретный ключ, и чтобы с помощью него получить код нужно сделать следующее:
Получить время:
auto clock = chrono::system_clock::now().time_since_epoch();
auto currentTime = chrono::duration_cast<chrono::milliseconds>(clock).count();
string time = Hex::timeInHex(currentTime, 30);
Вычислить HMAC-SHA1 хеш от времени и ключа:
auto timeBytes = Hex::hexStringToBytes(time);
auto keyBytes = Hex::hexStringToBytes(key);
auto hash = Hex::hmac("SHA1", keyBytes, timeBytes);
Получить последние 4 байта хеша и преобразовать в целое число:
int offset = hash[hash.size() - 1] & 0xF; // эквивалентно сдвигу вправо на 4 бита
int binary = ((hash[offset] & 0x7F) << 24)
| ((hash[offset + 1] & 0xFF) << 16)
| ((hash[offset + 2] & 0xFF) << 8)
| (hash[offset + 3] & 0xFF); // Значение из четырёх байтов
Разделить его на 10 в степени длины кода (обычно это 10⁶) и получить остаток:
int otp = binary % ((int) pow(10, digits));
Проверяем и получаем на выходе наш долгожданный код:
