@@ -65,8 +65,20 @@ import 'dart:math';
6565import 'dart:typed_data' ;
6666import 'package:cryptography/cryptography.dart' ;
6767
68+ /// Get the PBKDF iterations for this version
69+ /// https://cheatsheetseries.owasp.org/cheatsheets/Password_Storage_Cheat_Sheet.html#pbkdf2
70+ int getPbkdfIterations (int version) {
71+ switch (version) {
72+ case 1 :
73+ return 120000 ;
74+ case 2 :
75+ return 210000 ;
76+ default :
77+ throw VersionError ();
78+ }
79+ }
80+
6881/// Constants that should not be changed without good reason
69- const int pbkdfIterations = 120000 ; // OWASP recommendation: https://cheatsheetseries.owasp.org/cheatsheets/Password_Storage_Cheat_Sheet.html#pbkdf2
7082const int saltLength = 16 ; // in bytes
7183const String dataKeyDomain = 'STACK_WALLET_DATA_KEY' ;
7284const String encryptionDomain = 'STACK_WALLET_ENCRYPTION' ;
@@ -76,7 +88,7 @@ const String encryptionDomain = 'STACK_WALLET_ENCRYPTION';
7688///
7789
7890/// The provided passphrase is incorrect
79- class IncorrectPassphrase implements Exception {
91+ class IncorrectPassphraseOrVersion implements Exception {
8092 String errMsg () => 'Incorrect passphrase' ;
8193}
8294
@@ -95,6 +107,11 @@ class EncodingError implements Exception {
95107 String errMsg () => 'There was an encoding error' ;
96108}
97109
110+ /// Version is invalid
111+ class VersionError implements Exception {
112+ String errMsg () => 'Bad version' ;
113+ }
114+
98115///
99116/// StorageCryptoHandler
100117///
@@ -108,12 +125,12 @@ class StorageCryptoHandler {
108125 StorageCryptoHandler ._(this ._salt, this ._mainKey, this ._dataKey);
109126
110127 /// Create a new handler
111- static Future <StorageCryptoHandler > fromNewPassphrase (String passphrase) async {
128+ static Future <StorageCryptoHandler > fromNewPassphrase (String passphrase, int version ) async {
112129 // Generate a random salt
113130 final salt = _randomBytes (saltLength);
114131
115132 // Use the passphrase and salt to derive the main key with the PBKDF
116- final mainKey = await _pbkdf2 (salt, _stringToBytes (passphrase));
133+ final mainKey = await _pbkdf2 (salt, _stringToBytes (passphrase), version );
117134
118135 // Generate a random data key
119136 final dataKey = _randomBytes (Xchacha20 .poly1305Aead ().secretKeyLength);
@@ -122,8 +139,8 @@ class StorageCryptoHandler {
122139 return StorageCryptoHandler ._(salt, mainKey, dataKey);
123140 }
124141
125- /// Create a handler from an existing passphrase and key blob
126- static Future <StorageCryptoHandler > fromExisting (String passphrase, String keyBlob) async {
142+ /// Create a handler from an existing passphrase and key blob with a specified version
143+ static Future <StorageCryptoHandler > fromExisting (String passphrase, String keyBlob, int version ) async {
127144 // Decode the encrypted data key
128145 Uint8List keyBlobBytes = _stringToBytesBase64 (keyBlob);
129146 if (keyBlobBytes.length != saltLength + Xchacha20 .poly1305Aead ().nonceLength + Xchacha20 .poly1305Aead ().secretKeyLength + Poly1305 ().macLength) {
@@ -134,7 +151,7 @@ class StorageCryptoHandler {
134151 Uint8List encryptedDataKey = keyBlobBytes.sublist (saltLength);
135152
136153 // Derive the candidate main key
137- final Uint8List mainKey = await _pbkdf2 (salt, _stringToBytes (passphrase));
154+ final Uint8List mainKey = await _pbkdf2 (salt, _stringToBytes (passphrase), version );
138155
139156 // Determine if the main key is valid against the encrypted data key
140157 try {
@@ -156,17 +173,17 @@ class StorageCryptoHandler {
156173 // Assemble the handler
157174 return StorageCryptoHandler ._(salt, mainKey, dataKey);
158175 } on BadDecryption {
159- throw IncorrectPassphrase ();
176+ throw IncorrectPassphraseOrVersion ();
160177 }
161178 }
162179
163180 /// Reset the passphrase, which resets the salt and main key
164- Future <void > resetPassphrase (String passphrase) async {
181+ Future <void > resetPassphrase (String passphrase, version ) async {
165182 // Generate a random salt
166183 _salt = _randomBytes (saltLength);
167184
168185 // Use the passphrase and salt to derive the main key with the PBKDF
169- _mainKey = await _pbkdf2 (_salt, _stringToBytes (passphrase));
186+ _mainKey = await _pbkdf2 (_salt, _stringToBytes (passphrase), version );
170187 }
171188
172189 /// Get the key blob, which is safe to store
@@ -322,11 +339,11 @@ Uint8List _stringToBytesBase64(String data) {
322339}
323340
324341/// PBKDF2/SHA-512
325- Future <Uint8List > _pbkdf2 (Uint8List salt, Uint8List passphrase) async {
342+ Future <Uint8List > _pbkdf2 (Uint8List salt, Uint8List passphrase, int version ) async {
326343 // Set up the PBKDF
327344 final Pbkdf2 pbkdf = Pbkdf2 (
328345 macAlgorithm: Hmac .sha512 (),
329- iterations: pbkdfIterations ,
346+ iterations: getPbkdfIterations (version) ,
330347 bits: Xchacha20 .poly1305Aead ().secretKeyLength * 8 , // bytes to bits
331348 );
332349
0 commit comments