| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899 |
- import 'dart:math' show pow;
- import 'dart:typed_data' show Uint8List, Endian;
- import 'package:crypto/crypto.dart' show Hash, Hmac, sha1, sha256, sha512;
- import 'base32.dart' show Base32;
- /// Hash algorithm to OTP
- enum OtpHashAlgorithm {
- sha1,
- sha256,
- sha512;
- static OtpHashAlgorithm fromString(String str) {
- if (str == "sha1") {
- return OtpHashAlgorithm.sha1;
- } else if (str == "sha256") {
- return OtpHashAlgorithm.sha256;
- } else if (str == "sha512") {
- return OtpHashAlgorithm.sha512;
- }
- throw Exception("Unknown algorithm");
- }
- }
- extension _StringOperations on OtpHashAlgorithm {
- Hash toHashFunction() {
- if (this == OtpHashAlgorithm.sha1) {
- return sha1;
- } else if (this == OtpHashAlgorithm.sha256) {
- return sha256;
- } else if (this == OtpHashAlgorithm.sha512) {
- return sha512;
- }
- throw Exception("Unknown algorithm");
- }
- }
- /// Static class for generating TOTP codes.
- ///
- /// References:
- /// * RFC 4226, 6238
- /// * https://github.com/LanceGin/dotp/blob/master/lib/src/otp.dart
- /// * https://stackoverflow.com/questions/49398437
- class Totp {
- /// Formats a generated [code] to make it look nice
- static String prettyValue(String code) {
- // Length at which to split at
- int splitLength = code.length == 8 ? 4 : 3;
- // Combine 2 halves
- return '${code.substring(0, splitLength)} ${code.substring(splitLength)}';
- }
- /// Generates a TOTP value for the given attributes.
- ///
- /// Note:
- /// * [secret] should be a base32 string.
- /// * Currently only supports sha1 and sha256.
- static String generateCode(int time, String secret,
- [int digits = 6,
- int period = 30,
- OtpHashAlgorithm algorithm = OtpHashAlgorithm.sha1]) {
- // Calculate number of time steps between T0 (assumed to be Unix Epoch)
- // and current time
- int timeCounter = ((time - 0) / period).floor();
- // TOTP = HOTP(K, T)
- return _generateHOTP(secret, timeCounter, digits, algorithm);
- }
- /// Generate multiple TOTP values for the given [times].
- ///
- /// Currently only supports sha1 and sha256.
- static List<String> generateCodes(List<int> times, String secret,
- [int digits = 6,
- int period = 30,
- OtpHashAlgorithm algorithm = OtpHashAlgorithm.sha1]) {
- return times
- .map((time) => generateCode(time, secret, digits, period, algorithm))
- .toList();
- }
- /// Generates the HOTP code.
- static String _generateHOTP(
- String secret, int timeCounter, int digits, OtpHashAlgorithm algorithm) {
- var key = Base32.decode(secret);
- var bytes = Uint8List(8)
- ..buffer.asByteData().setInt64(0, timeCounter, Endian.big);
- // Determine algorithm
- var hmac = Hmac(algorithm.toHashFunction(), key);
- var digest = hmac.convert(bytes);
- int offset = digest.bytes[digest.bytes.length - 1] & 0xf;
- int binary = ((digest.bytes[offset] & 0x7f) << 24) |
- ((digest.bytes[offset + 1] & 0xff) << 16) |
- ((digest.bytes[offset + 2] & 0xff) << 8) |
- (digest.bytes[offset + 3] & 0xff);
- num otp = binary % (pow(10, digits));
- return otp.toString().padLeft(digits, "0");
- }
- }
|