| 1 | import 'dart:math'; |
| 2 | import 'package:base32/base32.dart'; |
| 3 | import 'package:crypto/crypto.dart'; |
| 4 | |
| 5 | import 'package:flutter/foundation.dart'; |
| 6 | |
| 7 | //*========================== TOTP 2FA Related Utilities ========================================== |
| 8 | |
| 9 | String generateRandomBase32SecretKey(int byteLength) { |
| 10 | final Random _secureRandom = Random.secure(); |
| 11 | // Generate random bytes |
| 12 | final randomBytes = Uint8List.fromList( |
| 13 | List<int>.generate(byteLength, (i) => _secureRandom.nextInt(256)), |
| 14 | ); |
| 15 | |
| 16 | // Encode bytes to base32 |
| 17 | final base32SecretKey = base32.encode(randomBytes); |
| 18 | |
| 19 | return base32SecretKey; |
| 20 | } |
| 21 | |
| 22 | String generateOTP({required String secretKey, required int input}) { |
| 23 | /// base32 decode the secret |
| 24 | var hmacKey = base32.decode(secretKey); |
| 25 | |
| 26 | /// initial the HMAC-SHA1 object |
| 27 | var hmacSha = Hmac(sha512, hmacKey); |
| 28 | |
| 29 | /// get hmac answer |
| 30 | var hmac = hmacSha.convert(intToBytelist(input: input)).bytes; |
| 31 | |
| 32 | /// calculate the init offset |
| 33 | int offset = hmac[hmac.length - 1] & 0xf; |
| 34 | |
| 35 | /// calculate the code |
| 36 | int code = ((hmac[offset] & 0x7f) << 24 | |
| 37 | (hmac[offset + 1] & 0xff) << 16 | |
| 38 | (hmac[offset + 2] & 0xff) << 8 | |
| 39 | (hmac[offset + 3] & 0xff)); |
| 40 | |
| 41 | /// get the initial string code |
| 42 | var strCode = (code % pow(10, 8)).toString(); |
| 43 | strCode = strCode.padLeft(8, '0'); |
| 44 | |
| 45 | return strCode; |
| 46 | } |
| 47 | |
| 48 | List<int> intToBytelist({required int input, int padding = 8}) { |
| 49 | List<int> _result = []; |
| 50 | var _input = input; |
| 51 | while (_input != 0) { |
| 52 | _result.add(_input & 0xff); |
| 53 | _input >>= padding; |
| 54 | } |
| 55 | _result.addAll(List<int>.generate(padding, (_) => 0)); |
| 56 | _result = _result.sublist(0, padding); |
| 57 | _result = _result.reversed.toList(); |
| 58 | return _result; |
| 59 | } |
| 60 | |
| 61 | String totpNow(String secretKey) { |
| 62 | int _formatTime = timeFormat(time: DateTime.now()); |
| 63 | return generateOTP(input: _formatTime, secretKey: secretKey); |
| 64 | } |
| 65 | |
| 66 | int timeFormat({required DateTime time}) { |
| 67 | final _timeStr = time.millisecondsSinceEpoch.toString(); |
| 68 | final _formatTime = _timeStr.substring(0, _timeStr.length - 3); |
| 69 | |
| 70 | return int.parse(_formatTime) ~/ 30; |
| 71 | } |
| 72 | |
| 73 | bool verify({String? otp, DateTime? time, required String secretKey}) { |
| 74 | if (otp == null) { |
| 75 | return false; |
| 76 | } |
| 77 | |
| 78 | var _time = time ?? DateTime.now(); |
| 79 | var _input = timeFormat(time: _time); |
| 80 | |
| 81 | String otpTime = generateOTP(input: _input, secretKey: secretKey); |
| 82 | return otp == otpTime; |
| 83 | } |