code.dart 3.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178
  1. import 'package:ente_auth/utils/totp_util.dart';
  2. class Code {
  3. static const defaultDigits = 6;
  4. static const defaultPeriod = 30;
  5. int? generatedID;
  6. final String account;
  7. final String issuer;
  8. final int digits;
  9. final int period;
  10. final String secret;
  11. final Algorithm algorithm;
  12. final Type type;
  13. final String rawData;
  14. bool? hasSynced;
  15. Code(
  16. this.account,
  17. this.issuer,
  18. this.digits,
  19. this.period,
  20. this.secret,
  21. this.algorithm,
  22. this.type,
  23. this.rawData, {
  24. this.generatedID,
  25. });
  26. static Code fromAccountAndSecret(
  27. String account,
  28. String issuer,
  29. String secret,
  30. ) {
  31. return Code(
  32. account,
  33. issuer,
  34. defaultDigits,
  35. defaultPeriod,
  36. secret,
  37. Algorithm.sha1,
  38. Type.totp,
  39. "otpauth://totp/" +
  40. issuer +
  41. ":" +
  42. account +
  43. "?algorithm=SHA1&digits=6&issuer=" +
  44. issuer +
  45. "&period=30&secret=" +
  46. secret,
  47. );
  48. }
  49. static Code fromRawData(String rawData) {
  50. Uri uri = Uri.parse(rawData);
  51. return Code(
  52. _getAccount(uri),
  53. _getIssuer(uri),
  54. _getDigits(uri),
  55. _getPeriod(uri),
  56. getSanitizedSecret(uri.queryParameters['secret']!),
  57. _getAlgorithm(uri),
  58. _getType(uri),
  59. rawData,
  60. );
  61. }
  62. static String _getAccount(Uri uri) {
  63. try {
  64. String path = Uri.decodeComponent(uri.path);
  65. if (path.startsWith("/")) {
  66. path = path.substring(1, path.length);
  67. }
  68. // Parse account name from documented auth URI
  69. // otpauth://totp/ACCOUNT?secret=SUPERSECRET&issuer=SERVICE
  70. if (uri.queryParameters.containsKey("issuer") && !path.contains(":")) {
  71. return path;
  72. }
  73. return path.split(':')[1];
  74. } catch (e) {
  75. return "";
  76. }
  77. }
  78. static String _getIssuer(Uri uri) {
  79. try {
  80. if (uri.queryParameters.containsKey("issuer")) {
  81. String issuerName = uri.queryParameters['issuer']!;
  82. // Handle issuer name with period
  83. // See https://github.com/ente-io/auth/pull/77
  84. if (issuerName.contains("period=")) {
  85. return issuerName.substring(0, issuerName.indexOf("period="));
  86. }
  87. return issuerName;
  88. }
  89. final String path = Uri.decodeComponent(uri.path);
  90. return path.split(':')[0].substring(1);
  91. } catch (e) {
  92. return "";
  93. }
  94. }
  95. static int _getDigits(Uri uri) {
  96. try {
  97. return int.parse(uri.queryParameters['digits']!);
  98. } catch (e) {
  99. return defaultDigits;
  100. }
  101. }
  102. static int _getPeriod(Uri uri) {
  103. try {
  104. return int.parse(uri.queryParameters['period']!);
  105. } catch (e) {
  106. return defaultPeriod;
  107. }
  108. }
  109. static Algorithm _getAlgorithm(Uri uri) {
  110. try {
  111. final algorithm =
  112. uri.queryParameters['algorithm'].toString().toLowerCase();
  113. if (algorithm == "sha256") {
  114. return Algorithm.sha256;
  115. } else if (algorithm == "sha512") {
  116. return Algorithm.sha512;
  117. }
  118. } catch (e) {
  119. // nothing
  120. }
  121. return Algorithm.sha1;
  122. }
  123. static Type _getType(Uri uri) {
  124. if (uri.host == "totp") {
  125. return Type.totp;
  126. } else if (uri.host == "hotp") {
  127. return Type.hotp;
  128. }
  129. throw UnsupportedError("Unsupported format with host ${uri.host}");
  130. }
  131. @override
  132. bool operator ==(Object other) {
  133. if (identical(this, other)) return true;
  134. return other is Code &&
  135. other.account == account &&
  136. other.issuer == issuer &&
  137. other.digits == digits &&
  138. other.period == period &&
  139. other.secret == secret &&
  140. other.type == type &&
  141. other.rawData == rawData;
  142. }
  143. @override
  144. int get hashCode {
  145. return account.hashCode ^
  146. issuer.hashCode ^
  147. digits.hashCode ^
  148. period.hashCode ^
  149. secret.hashCode ^
  150. type.hashCode ^
  151. rawData.hashCode;
  152. }
  153. }
  154. enum Type {
  155. totp,
  156. hotp,
  157. }
  158. enum Algorithm {
  159. sha1,
  160. sha256,
  161. sha512,
  162. }