Skip to content

Commit 5574431

Browse files
committed
refactor: CryptoStore.createHmacKey() is now CryptoStore.hmacSecretKey. Also dropped support of memEquals in favor of hashlib isEqual.
1 parent a469a5c commit 5574431

4 files changed

Lines changed: 9 additions & 52 deletions

File tree

lib/model/backup.dart

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@ import 'dart:async';
22
import 'dart:convert';
33
import 'dart:io';
44

5-
import 'package:cipherlib/hashlib.dart';
65
import 'package:flutter/foundation.dart';
76
import 'package:flutter_riverpod/flutter_riverpod.dart';
87
import 'package:open_authenticator/app.dart';
@@ -148,8 +147,7 @@ class Backup implements Comparable<Backup> {
148147

149148
Map<String, dynamic> jsonData = jsonDecode(file.readAsStringSync());
150149
CryptoStore cryptoStore = CryptoStore.fromPassword(password, Salt.fromRawValue(value: base64.decode(jsonData[kSaltKey])));
151-
MACHashBase hmacSecretKey = cryptoStore.createHmacKey();
152-
if (!(hmacSecretKey.verify(base64.decode(jsonData[kPasswordSignatureKey]), utf8.encode(password)))) {
150+
if (!(cryptoStore.hmacSecretKey.verify(base64.decode(jsonData[kPasswordSignatureKey]), utf8.encode(password)))) {
153151
throw InvalidPasswordException();
154152
}
155153

@@ -201,11 +199,10 @@ class Backup implements Comparable<Backup> {
201199
DecryptedTotp? decryptedTotp = totp.changeEncryptionKey(currentCryptoStore, newStore);
202200
toBackup.add(decryptedTotp ?? totp);
203201
}
204-
MACHashBase hmacSecretKey = newStore.createHmacKey();
205202
File file = await getBackupPath(createDirectory: true);
206203
await file.writeAsString(
207204
jsonEncode({
208-
kPasswordSignatureKey: hmacSecretKey.string(password, utf8).base64(),
205+
kPasswordSignatureKey: newStore.hmacSecretKey.string(password, utf8).base64(),
209206
kSaltKey: base64.encode(newStore.salt.value),
210207
kTotpsKey: [
211208
for (Totp totp in toBackup) totp.toJson(),

lib/model/crypto.dart

Lines changed: 5 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -110,20 +110,20 @@ class CryptoStore {
110110

111111
/// Creates a [CryptoStoreWithPasswordSignature] from the given [password].
112112
CryptoStore.fromPassword(String password, Salt salt) : this._(
113-
key: _deriveKey(password, salt),
113+
key: _deriveKey(password, salt).bytes,
114114
salt: salt,
115115
);
116116

117117
/// Generates a derived key from the given [password] and save it to the device secure storage.
118118
/// Also returns the salt that has been used.
119-
static Uint8List _deriveKey(String password, Salt salt) {
119+
static Argon2HashDigest _deriveKey(String password, Salt salt) {
120120
Argon2 argon2 = Argon2(
121121
iterations: Argon2Parameters.iterations,
122122
memorySizeKB: Argon2Parameters.memorySize,
123123
parallelism: Argon2Parameters.parallelism,
124124
salt: salt.value,
125125
);
126-
return argon2.convert(utf8.encode(password)).bytes;
126+
return argon2.convert(utf8.encode(password));
127127
}
128128

129129
/// Encrypts the given text.
@@ -151,10 +151,7 @@ class CryptoStore {
151151
}
152152

153153
/// Checks if the given password is valid.
154-
bool checkPasswordValidity(String password) {
155-
Uint8List derivedKey = _deriveKey(password, salt);
156-
return memEquals(derivedKey, key);
157-
}
154+
bool checkPasswordValidity(String password) => _deriveKey(password, salt).isEqual(key);
158155

159156
/// Checks if the given [encryptedData] could be decrypted using [decrypt].
160157
/// There seems to be no better way of doing this.
@@ -164,7 +161,7 @@ class CryptoStore {
164161
bool canDecrypt(Uint8List encryptedData) => decrypt(encryptedData) != null;
165162

166163
/// Returns the HMAC secret key corresponding to the [key].
167-
MACHashBase createHmacKey() => sha256.hmac.by(key);
164+
MACHashBase get hmacSecretKey => sha256.hmac.by(key);
168165
}
169166

170167
/// Represents a decoded salt.

lib/model/password_verification/methods/password_signature.dart

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,6 @@
11
import 'dart:async';
22
import 'dart:convert';
3-
import 'dart:typed_data';
43

5-
import 'package:cipherlib/hashlib.dart';
64
import 'package:flutter_riverpod/flutter_riverpod.dart';
75
import 'package:open_authenticator/model/crypto.dart';
86
import 'package:open_authenticator/model/password_verification/methods/method.dart';
@@ -48,8 +46,7 @@ class PasswordSignatureVerificationMethodNotifier extends AsyncNotifier<Password
4846
/// Generates the [password] signature with the given [salt].
4947
Future<String> _generatePasswordSignature(String password, Salt salt) async {
5048
CryptoStore cryptoStore = CryptoStore.fromPassword(password, salt);
51-
MACHashBase hmacSecretKey = cryptoStore.createHmacKey();
52-
String passwordSignature = hmacSecretKey.string(password, utf8).base64();
49+
String passwordSignature = cryptoStore.hmacSecretKey.string(password, utf8).base64();
5350
return passwordSignature;
5451
}
5552
}
@@ -77,8 +74,6 @@ class PasswordSignatureVerificationMethod with PasswordVerificationMethod {
7774
return false;
7875
}
7976
CryptoStore cryptoStore = CryptoStore.fromPassword(password, salt);
80-
MACHashBase hmacSecretKey = cryptoStore.createHmacKey();
81-
Uint8List decodedSignature = base64.decode(passwordSignature!);
82-
return hmacSecretKey.verify(decodedSignature, utf8.encode(password));
77+
return cryptoStore.hmacSecretKey.verify(base64.decode(passwordSignature!), utf8.encode(password));
8378
}
8479
}

lib/utils/utils.dart

Lines changed: 0 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -44,38 +44,6 @@ void handleException(Object? ex, StackTrace? stackTrace, {bool? sendToSentry}) {
4444
/// Returns whether the given type [S] is a subtype of type [T].
4545
bool isSubtype<S, T>() => <S>[] is List<T>;
4646

47-
/// Compares two [Uint8List]s by comparing 8 bytes at a time.
48-
/// Kudos to https://stackoverflow.com/a/70751501/3608831.
49-
bool memEquals(Uint8List bytes1, Uint8List bytes2) {
50-
if (identical(bytes1, bytes2)) {
51-
return true;
52-
}
53-
54-
if (bytes1.lengthInBytes != bytes2.lengthInBytes) {
55-
return false;
56-
}
57-
58-
// Treat the original byte lists as lists of 8-byte words.
59-
var numWords = bytes1.lengthInBytes ~/ 8;
60-
var words1 = bytes1.buffer.asUint64List(0, numWords);
61-
var words2 = bytes2.buffer.asUint64List(0, numWords);
62-
63-
for (var i = 0; i < words1.length; i += 1) {
64-
if (words1[i] != words2[i]) {
65-
return false;
66-
}
67-
}
68-
69-
// Compare any remaining bytes.
70-
for (var i = words1.lengthInBytes; i < bytes1.lengthInBytes; i += 1) {
71-
if (bytes1[i] != bytes2[i]) {
72-
return false;
73-
}
74-
}
75-
76-
return true;
77-
}
78-
7947
/// A simple transparent image. Represented as a Uint8List, which was originally extracted from the Flutter codebase.
8048
Uint8List kTransparentImage = Uint8List.fromList([
8149
0x89,

0 commit comments

Comments
 (0)