123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225 |
- import 'package:ente_auth/utils/totp_util.dart';
- class Code {
- static const defaultDigits = 6;
- static const defaultPeriod = 30;
- int? generatedID;
- final String account;
- final String issuer;
- final int digits;
- final int period;
- final String secret;
- final Algorithm algorithm;
- final Type type;
- final String rawData;
- bool? hasSynced;
- Code(
- this.account,
- this.issuer,
- this.digits,
- this.period,
- this.secret,
- this.algorithm,
- this.type,
- this.rawData, {
- this.generatedID,
- });
- Code copyWith({
- String? account,
- String? issuer,
- int? digits,
- int? period,
- String? secret,
- Algorithm? algorithm,
- Type? type,
- }) {
- final String updateAccount = account ?? this.account;
- final String updateIssuer = issuer ?? this.issuer;
- final int updatedDigits = digits ?? this.digits;
- final int updatePeriod = period ?? this.period;
- final String updatedSecret = secret ?? this.secret;
- final Algorithm updatedAlgo = algorithm ?? this.algorithm;
- final Type updatedType = type ?? this.type;
- return Code(
- updateAccount,
- updateIssuer,
- updatedDigits,
- updatePeriod,
- updatedSecret,
- updatedAlgo,
- updatedType,
- "otpauth://${updatedType.name}/" +
- updateIssuer +
- ":" +
- updateAccount +
- "?algorithm=${updatedAlgo.name}&digits=$updatedDigits&issuer=" +
- updateIssuer +
- "&period=$updatePeriod&secret=" +
- updatedSecret,
- generatedID: generatedID,
- );
- }
- static Code fromAccountAndSecret(
- String account,
- String issuer,
- String secret,
- ) {
- return Code(
- account,
- issuer,
- defaultDigits,
- defaultPeriod,
- secret,
- Algorithm.sha1,
- Type.totp,
- "otpauth://totp/" +
- issuer +
- ":" +
- account +
- "?algorithm=SHA1&digits=6&issuer=" +
- issuer +
- "&period=30&secret=" +
- secret,
- );
- }
- static Code fromRawData(String rawData) {
- Uri uri = Uri.parse(rawData);
- try {
- return Code(
- _getAccount(uri),
- _getIssuer(uri),
- _getDigits(uri),
- _getPeriod(uri),
- getSanitizedSecret(uri.queryParameters['secret']!),
- _getAlgorithm(uri),
- _getType(uri),
- rawData,
- );
- } catch(e) {
- // if account name contains # without encoding,
- // rest of the url are treated as url fragment
- if(rawData.contains("#")) {
- return Code.fromRawData(rawData.replaceAll("#", '%23'));
- } else {
- rethrow;
- }
- }
- }
- static String _getAccount(Uri uri) {
- try {
- String path = Uri.decodeComponent(uri.path);
- if (path.startsWith("/")) {
- path = path.substring(1, path.length);
- }
- // Parse account name from documented auth URI
- // otpauth://totp/ACCOUNT?secret=SUPERSECRET&issuer=SERVICE
- if (uri.queryParameters.containsKey("issuer") && !path.contains(":")) {
- return path;
- }
- return path.split(':')[1];
- } catch (e) {
- return "";
- }
- }
- static String _getIssuer(Uri uri) {
- try {
- if (uri.queryParameters.containsKey("issuer")) {
- String issuerName = uri.queryParameters['issuer']!;
- // Handle issuer name with period
- // See https://github.com/ente-io/auth/pull/77
- if (issuerName.contains("period=")) {
- return issuerName.substring(0, issuerName.indexOf("period="));
- }
- return issuerName;
- }
- final String path = Uri.decodeComponent(uri.path);
- return path.split(':')[0].substring(1);
- } catch (e) {
- return "";
- }
- }
- static int _getDigits(Uri uri) {
- try {
- return int.parse(uri.queryParameters['digits']!);
- } catch (e) {
- return defaultDigits;
- }
- }
- static int _getPeriod(Uri uri) {
- try {
- return int.parse(uri.queryParameters['period']!);
- } catch (e) {
- return defaultPeriod;
- }
- }
- static Algorithm _getAlgorithm(Uri uri) {
- try {
- final algorithm =
- uri.queryParameters['algorithm'].toString().toLowerCase();
- if (algorithm == "sha256") {
- return Algorithm.sha256;
- } else if (algorithm == "sha512") {
- return Algorithm.sha512;
- }
- } catch (e) {
- // nothing
- }
- return Algorithm.sha1;
- }
- static Type _getType(Uri uri) {
- if (uri.host == "totp") {
- return Type.totp;
- } else if (uri.host == "hotp") {
- return Type.hotp;
- }
- throw UnsupportedError("Unsupported format with host ${uri.host}");
- }
- @override
- bool operator ==(Object other) {
- if (identical(this, other)) return true;
- return other is Code &&
- other.account == account &&
- other.issuer == issuer &&
- other.digits == digits &&
- other.period == period &&
- other.secret == secret &&
- other.type == type &&
- other.rawData == rawData;
- }
- @override
- int get hashCode {
- return account.hashCode ^
- issuer.hashCode ^
- digits.hashCode ^
- period.hashCode ^
- secret.hashCode ^
- type.hashCode ^
- rawData.hashCode;
- }
- }
- enum Type {
- totp,
- hotp,
- }
- enum Algorithm {
- sha1,
- sha256,
- sha512,
- }
|