cake_wallet/lib/utils/totp_utils.dart
Adegoke David 43e062d1ac
Cw-263-TOTP-2FA-In-Security-Settings (#892)
* CW-263-TOTP-2FA-in-security-settings WIP

* Implement TOTP 2FA WIP

* Implement TOTP 2FA Authentication

* chore: Remove unneeded formatting

* revert formatting

* fixes

* CW-263-TOTP-2FA-in-security-settings WIP

* Setup TOTP Complete, left with Modify TOTF

* CW-263-TOTP-2FA-in-security-settings

* CW-263-TOTP-2FA-in-security-settings

* CW-263-TOTP-2FA-in-security-settings

* fix: Add copy-to-clipboard for qr secret key

* fix: Translation

* chore: Move strings into translation files

* feat: End to end flow for TOTP

* hotfix: Switch totp to use sha512

* Update strings; 8 digits and error explanation

* fix: Totp 2fa implementation feedback

* hotfix: same action for button and alert close

* feat: App should show both normal and totp auths when totp is enabled

* hotfix: prevent barrier from dismissing

* fix: Changes requested during PR review

* - Minor Enhancements
- Minor UI fixes

---------

Co-authored-by: Justin Ehrenhofer <justin.ehrenhofer@gmail.com>
Co-authored-by: OmarHatem <omarh.ismail1@gmail.com>
2023-05-17 17:43:23 +03:00

83 lines
2.3 KiB
Dart

import 'dart:math';
import 'package:base32/base32.dart';
import 'package:crypto/crypto.dart';
import 'package:flutter/foundation.dart';
//*========================== TOTP 2FA Related Utilities ==========================================
String generateRandomBase32SecretKey(int byteLength) {
final Random _secureRandom = Random.secure();
// Generate random bytes
final randomBytes = Uint8List.fromList(
List<int>.generate(byteLength, (i) => _secureRandom.nextInt(256)),
);
// Encode bytes to base32
final base32SecretKey = base32.encode(randomBytes);
return base32SecretKey;
}
String generateOTP({required String secretKey, required int input}) {
/// base32 decode the secret
var hmacKey = base32.decode(secretKey);
/// initial the HMAC-SHA1 object
var hmacSha = Hmac(sha512, hmacKey);
/// get hmac answer
var hmac = hmacSha.convert(intToBytelist(input: input)).bytes;
/// calculate the init offset
int offset = hmac[hmac.length - 1] & 0xf;
/// calculate the code
int code = ((hmac[offset] & 0x7f) << 24 |
(hmac[offset + 1] & 0xff) << 16 |
(hmac[offset + 2] & 0xff) << 8 |
(hmac[offset + 3] & 0xff));
/// get the initial string code
var strCode = (code % pow(10, 8)).toString();
strCode = strCode.padLeft(8, '0');
return strCode;
}
List<int> intToBytelist({required int input, int padding = 8}) {
List<int> _result = [];
var _input = input;
while (_input != 0) {
_result.add(_input & 0xff);
_input >>= padding;
}
_result.addAll(List<int>.generate(padding, (_) => 0));
_result = _result.sublist(0, padding);
_result = _result.reversed.toList();
return _result;
}
String totpNow(String secretKey) {
int _formatTime = timeFormat(time: DateTime.now());
return generateOTP(input: _formatTime, secretKey: secretKey);
}
int timeFormat({required DateTime time}) {
final _timeStr = time.millisecondsSinceEpoch.toString();
final _formatTime = _timeStr.substring(0, _timeStr.length - 3);
return int.parse(_formatTime) ~/ 30;
}
bool verify({String? otp, DateTime? time, required String secretKey}) {
if (otp == null) {
return false;
}
var _time = time ?? DateTime.now();
var _input = timeFormat(time: _time);
String otpTime = generateOTP(input: _input, secretKey: secretKey);
return otp == otpTime;
}