Add support for incrementing hotp counter

This commit is contained in:
Neeraj Gupta 2023-08-01 17:34:58 +05:30
parent 80d97734fd
commit e401503948
4 changed files with 80 additions and 33 deletions

View file

@ -64,7 +64,7 @@ class Code {
"?algorithm=${updatedAlgo.name}&digits=$updatedDigits&issuer=" +
updateIssuer +
"&period=$updatePeriod&secret=" +
updatedSecret,
updatedSecret + (updatedType == Type.hotp ? "&counter=$updatedCounter" : ""),
generatedID: generatedID,
);
}
@ -216,6 +216,7 @@ class Code {
other.digits == digits &&
other.period == period &&
other.secret == secret &&
other.counter == counter &&
other.type == type &&
other.rawData == rawData;
}
@ -228,6 +229,7 @@ class Code {
period.hashCode ^
secret.hashCode ^
type.hashCode ^
counter.hashCode ^
rawData.hashCode;
}
}

View file

@ -132,7 +132,7 @@ class _SetupEnterSecretKeyPageState extends State<SetupEnterSecretKeyPage> {
secret: secret,
);
// Verify the validity of the code
getTotp(newCode);
getOTP(newCode);
Navigator.of(context).pop(newCode);
} catch (e) {
_showIncorrectDetailsDialog(context);

View file

@ -35,10 +35,12 @@ class _CodeWidgetState extends State<CodeWidget> {
_everySecondTimer =
Timer.periodic(const Duration(milliseconds: 500), (Timer t) {
String newCode = _getTotp();
String newCode = _getCurrentOTP();
if (newCode != _currentCode.value) {
_currentCode.value = newCode;
_nextCode.value = _getNextTotp();
if (widget.code.type == Type.totp) {
_nextCode.value = _getNextTotp();
}
}
});
}
@ -54,8 +56,10 @@ class _CodeWidgetState extends State<CodeWidget> {
@override
Widget build(BuildContext context) {
if (!_isInitialized) {
_currentCode.value = _getTotp();
_nextCode.value = _getNextTotp();
_currentCode.value = _getCurrentOTP();
if (widget.code.type == Type.totp) {
_nextCode.value = _getNextTotp();
}
_isInitialized = true;
}
final l10n = context.l10n;
@ -113,9 +117,10 @@ class _CodeWidgetState extends State<CodeWidget> {
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.center,
children: [
CodeTimerProgress(
period: widget.code.period,
),
if (widget.code.type == Type.totp)
CodeTimerProgress(
period: widget.code.period,
),
const SizedBox(
height: 16,
),
@ -174,27 +179,47 @@ class _CodeWidgetState extends State<CodeWidget> {
},
),
),
Column(
crossAxisAlignment: CrossAxisAlignment.end,
children: [
Text(
l10n.nextTotpTitle,
style: Theme.of(context).textTheme.caption,
),
ValueListenableBuilder<String>(
valueListenable: _nextCode,
builder: (context, value, child) {
return Text(
value,
style: const TextStyle(
fontSize: 18,
color: Colors.grey,
widget.code.type == Type.totp
? Column(
crossAxisAlignment: CrossAxisAlignment.end,
children: [
Text(
l10n.nextTotpTitle,
style:
Theme.of(context).textTheme.caption,
),
);
},
),
],
),
ValueListenableBuilder<String>(
valueListenable: _nextCode,
builder: (context, value, child) {
return Text(
value,
style: const TextStyle(
fontSize: 18,
color: Colors.grey,
),
);
},
),
],
)
: Column(
crossAxisAlignment: CrossAxisAlignment.end,
children: [
Text(
l10n.nextTotpTitle,
style:
Theme.of(context).textTheme.caption,
),
InkWell(
onTap: _onNextHotpTapped,
child: const Icon(
Icons.forward_outlined,
size: 32,
color: Colors.grey,
),
),
],
),
],
),
),
@ -213,10 +238,16 @@ class _CodeWidgetState extends State<CodeWidget> {
}
void _copyToClipboard() {
FlutterClipboard.copy(_getTotp())
FlutterClipboard.copy(_getCurrentOTP())
.then((value) => showToast(context, context.l10n.copiedToClipboard));
}
void _onNextHotpTapped() {
if(widget.code.type == Type.hotp) {
CodeStore.instance.addCode(widget.code.copyWith(counter: widget.code.counter + 1), shouldSync: true).ignore();
}
}
Future<void> _onEditPressed(_) async {
final Code? code = await Navigator.of(context).push(
MaterialPageRoute(
@ -287,9 +318,9 @@ class _CodeWidgetState extends State<CodeWidget> {
}
}
String _getTotp() {
String _getCurrentOTP() {
try {
return getTotp(widget.code);
return getOTP(widget.code);
} catch (e) {
return context.l10n.error;
}
@ -297,6 +328,7 @@ class _CodeWidgetState extends State<CodeWidget> {
String _getNextTotp() {
try {
assert(widget.code.type == Type.totp);
return getNextTotp(widget.code);
} catch (e) {
return context.l10n.error;

View file

@ -1,7 +1,10 @@
import 'package:ente_auth/models/code.dart';
import 'package:otp/otp.dart' as otp;
String getTotp(Code code) {
String getOTP(Code code) {
if(code.type == Type.hotp) {
return _getHOTPCode(code);
}
return otp.OTP.generateTOTPCodeString(
getSanitizedSecret(code.secret),
DateTime.now().millisecondsSinceEpoch,
@ -12,6 +15,16 @@ String getTotp(Code code) {
);
}
String _getHOTPCode(Code code) {
return otp.OTP.generateHOTPCodeString(
getSanitizedSecret(code.secret),
code.counter,
length: code.digits,
algorithm: _getAlgorithm(code),
isGoogle: true,
);
}
String getNextTotp(Code code) {
return otp.OTP.generateTOTPCodeString(
getSanitizedSecret(code.secret),