Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ jobs:
channel: 'stable'

- run: dart test
working-directory: ./totp
working-directory: ./otp

- run: dart test
working-directory: ./state
Expand Down
14 changes: 7 additions & 7 deletions lib/pages/add.dart
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import '../state/app_state.dart';
import '../ui/adaptive.dart'
show AdaptiveDialogAction, AppScaffold, isPlatformAndroid;
import 'package:another_authenticator_totp/totp_algorithm.dart';
import 'package:another_authenticator_totp/totp.dart' show Base32, TotpItem;
import 'package:another_authenticator_otp/otp.dart'
show Base32, OtpHashAlgorithm, OtpItem, OtpType;
import 'package:flutter/material.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
Expand Down Expand Up @@ -56,7 +56,7 @@ class _AddPageState extends State<AddPage> {
}

// Adds item or display errors
void addItem(TotpItem item) {
void addItem(OtpItem item) {
try {
Provider.of<AppState>(context, listen: false).addItem(item).then((_) {
Navigator.pop(context);
Expand Down Expand Up @@ -117,14 +117,14 @@ class _AddPageState extends State<AddPage> {
return;
}

// Initialise and add TOTP item
var item = TotpItem(
var item = OtpItem(
OtpType.totp,
_secretController.text,
"${_issuerController.text}:${_accountNameController.text}",
_digits,
_period,
OtpHashAlgorithm.sha1,
_issuerController.text,
_accountNameController.text);
_issuerController.text);

addItem(item);
}
Expand Down
4 changes: 2 additions & 2 deletions lib/pages/android/edit_list_item.dart
Original file line number Diff line number Diff line change
Expand Up @@ -52,15 +52,15 @@ class _EditListItem extends State<EditListItem> {
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
// Issuer
Text(widget.item.totp.issuer,
Text(widget.item.totp.getIssuer() ?? "",
style: Theme.of(context).textTheme.titleMedium),
// Generated code
Padding(
padding: const EdgeInsets.symmetric(vertical: 10),
child: Text(widget.item.totp.placeholder,
style: Theme.of(context).textTheme.displaySmall)),
// Account name
Text(widget.item.totp.accountName,
Text(widget.item.totp.getAccountName(),
style: Theme.of(context).textTheme.bodyMedium)
],
),
Expand Down
6 changes: 3 additions & 3 deletions lib/pages/android/list_item.dart
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ class _TOTPListItemState extends State<HomeListItem>
// Define animation
// Adapted from progress_indicator_demo.dart from flutter examples
_controller = AnimationController(
duration: Duration(seconds: widget.item.totp.period),
duration: Duration(seconds: widget.item.totp.getPeriod()),
lowerBound: 0.0,
upperBound: 1.0,
vsync: this,
Expand Down Expand Up @@ -87,15 +87,15 @@ class _TOTPListItemState extends State<HomeListItem>
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
// Issuer
Text(widget.item.totp.issuer,
Text(widget.item.totp.getIssuer() ?? "",
style: Theme.of(context).textTheme.titleMedium),
// Generated code
Padding(
padding: const EdgeInsets.symmetric(vertical: 10),
child: Text(_code,
style: Theme.of(context).textTheme.displaySmall)),
// Account name
Text(widget.item.totp.accountName,
Text(widget.item.totp.getAccountName(),
style: Theme.of(context).textTheme.bodyMedium)
],
),
Expand Down
10 changes: 7 additions & 3 deletions lib/pages/qr.dart
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import 'dart:async' show Future;
import '../config/routes.dart';
import '../state/app_state.dart';
import 'package:another_authenticator/config/routes.dart';
import 'package:another_authenticator/state/app_state.dart';
import 'package:another_authenticator_otp/models/otp_type.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart' show PlatformException;
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
Expand All @@ -25,8 +26,11 @@ class _ScanQRPageState extends State<ScanQRPage> {
(_) async {
try {
final value = await _scan();
// Parse scanned value into item and pop
// Parse scanned value into item
var item = BaseItemType.newAuthenticatorItemFromUri(value);
if (item.totp.type == OtpType.hotp) {
throw Exception('HOTP not supported by UI');
}
// Pop until scan page
Navigator.of(context)
.popUntil(ModalRoute.withName(AppRoutes.addScan));
Expand Down
2 changes: 1 addition & 1 deletion lib/pages/shared/list_item_base.dart
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ abstract class TotpListItemBase extends StatefulWidget {

/// Progress indicator value.
double get indicatorValue {
return (_secondsSinceEpoch % item.totp.period) / item.totp.period;
return (_secondsSinceEpoch % item.totp.getPeriod()) / item.totp.getPeriod();
}

/// Returns the code of the item for the current time.
Expand Down
12 changes: 6 additions & 6 deletions lib/state/app_state.dart
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import 'package:another_authenticator_state/state.dart';
import 'package:another_authenticator_totp/totp.dart';
import 'package:another_authenticator_otp/otp.dart';
import 'package:flutter/widgets.dart';
import 'package:collection/collection.dart' show ListEquality;

Expand All @@ -12,10 +12,10 @@ class AppState extends ChangeNotifier {

AppState(this._repository);

/// List of TOTP items (internal implementation).
/// List of items (internal implementation).
List<BaseItemType>? _items;

/// TOTP items as list.
/// Items as list.
List<BaseItemType>? get items {
if (_items == null) {
loadItems();
Expand All @@ -28,13 +28,13 @@ class AppState extends ChangeNotifier {
notifyListeners();
}

/// Adds a TOTP item to the list.
Future addItem(TotpItem item) async {
/// Adds an item to the list.
Future addItem(OtpItem item) async {
await _repository.addItem(item);
await loadItems();
}

/// Replace list of TOTP items.
/// Replace list of items.
Future replaceItems(List<BaseItemType> items) async {
await _repository.replaceItems(items);
await loadItems();
Expand Down
File renamed without changes.
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import 'package:another_authenticator_totp/totp_algorithm.dart';
import 'package:another_authenticator_otp/otp.dart';

void main() {
print(Totp.generateCode(1542791843, "JBSWY3DPEHPK3PXP"));
Expand Down
4 changes: 4 additions & 0 deletions otp/lib/generator/code_generator_base.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
abstract interface class OtpCodeGeneratorBase {
String generateCode(int time);
int getPeriod();
}
27 changes: 27 additions & 0 deletions otp/lib/generator/totp_code_generator.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import 'code_generator_base.dart';
import '../models/otp_algorithm.dart';
import '../otp/totp_algorithm.dart';

class TotpCodeGenerator implements OtpCodeGeneratorBase {
final String secret;
final int? digits;
final int? period;
final OtpHashAlgorithm? algorithm;

static const _defaultDigits = 6;
static const _defaultAlgorithm = OtpHashAlgorithm.sha1;
static const _defaultPeriod = 30;

TotpCodeGenerator(this.secret, [this.digits, this.period, this.algorithm]);

@override
String generateCode(int time) {
return Totp.generateCode(time, secret, digits ?? _defaultDigits,
period ?? _defaultPeriod, algorithm ?? _defaultAlgorithm);
}

@override
int getPeriod() {
return period ?? _defaultPeriod;
}
}
36 changes: 36 additions & 0 deletions otp/lib/models/otp_algorithm.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import 'package:crypto/crypto.dart';

/// Hash algorithm to OTP
enum OtpHashAlgorithm {
sha1,
sha256,
sha512;

static OtpHashAlgorithm fromString(String str) {
if (str.toLowerCase() == "sha1") {
return OtpHashAlgorithm.sha1;
} else if (str.toLowerCase() == "sha256") {
return OtpHashAlgorithm.sha256;
} else if (str.toLowerCase() == "sha512") {
return OtpHashAlgorithm.sha512;
}
throw Exception("Unsupported algorithm");
}

static bool isValid(String str) {
return OtpHashAlgorithm.values.any((v) => v.name == str.toLowerCase());
}
}

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("Unsupported algorithm");
}
}
Loading