Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(cat-voices): vault crypto #1011

Merged
merged 25 commits into from
Oct 17, 2024
Merged
Show file tree
Hide file tree
Changes from 17 commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
3882326
chore: wip
damian-molinski Oct 15, 2024
e5a9e84
feat: first iteration of AesCryptoService
damian-molinski Oct 16, 2024
85cd4e5
feat: refactor SecureStorageVault to use CryptoService
damian-molinski Oct 16, 2024
d20746a
fix: keychain clearing vault
damian-molinski Oct 16, 2024
48585e8
chore: spacing in SecureStorageVault
damian-molinski Oct 16, 2024
7dd93b2
test: AesCryptoService
damian-molinski Oct 16, 2024
a969600
chore: wip
damian-molinski Oct 16, 2024
80e6da0
feat: vault crypto service versioning + algorithm ID
damian-molinski Oct 16, 2024
054aa4e
test: fix vault tests
damian-molinski Oct 16, 2024
5ead182
Merge branch 'main' into feat/vault_crypto_1001
damian-molinski Oct 16, 2024
48a1991
fix: vault singleton registration
damian-molinski Oct 16, 2024
e58608c
Merge branch 'main' into feat/vault_crypto_1001
damian-molinski Oct 16, 2024
e4022b2
fix: keychain adjustments
damian-molinski Oct 16, 2024
bc9db8c
chore: benchmark crypto service
damian-molinski Oct 16, 2024
0e2251b
fix: Use Pbkdf2 instead of Argon2id key derivation algorithm
damian-molinski Oct 17, 2024
bd85d1d
docs: improve docs for CryptoService, VaultCryptoService and Keychain
damian-molinski Oct 17, 2024
6c56130
Merge branch 'main' into feat/vault_crypto_1001
damian-molinski Oct 17, 2024
593dd2a
Update catalyst_voices/packages/catalyst_voices_models/lib/src/errors…
damian-molinski Oct 17, 2024
fc5a278
fix: missing props in exceptions
damian-molinski Oct 17, 2024
d1c3a82
Merge branch 'feat/vault_crypto_1001' of github.com:input-output-hk/c…
damian-molinski Oct 17, 2024
ba99f6e
docs: CryptoService and KeyDerivation connection
damian-molinski Oct 17, 2024
dd7542a
change random number to max 255
damian-molinski Oct 17, 2024
6d68f7c
fix: typo
damian-molinski Oct 17, 2024
f730ef0
Merge branch 'main' into feat/vault_crypto_1001
damian-molinski Oct 17, 2024
d068af3
Merge branch 'main' into feat/vault_crypto_1001
dtscalac Oct 17, 2024
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
1 change: 1 addition & 0 deletions .config/dictionaries/project.dic
Original file line number Diff line number Diff line change
Expand Up @@ -298,3 +298,4 @@ xctestrun
xcworkspace
xvfb
yoroi
Pbkdf2
2 changes: 1 addition & 1 deletion catalyst_voices/lib/dependency/dependencies.dart
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ final class Dependencies extends DependencyProvider {

void _registerServices() {
registerLazySingleton<Storage>(() => const SecureStorage());
registerLazySingleton<Vault>(() => const SecureStorageVault());
registerLazySingleton<Vault>(SecureStorageVault.new);
registerLazySingleton<DummyAuthStorage>(
() => const SecureDummyAuthStorage(),
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,12 +29,6 @@ final class SessionBloc extends Bloc<SessionEvent, SessionState>
) async {
if (!await _keychain.hasSeedPhrase) {
emit(const VisitorSessionState());
} else if (await _keychain.isUnlocked) {
// TODO(damian-molinski): we shouldn't keep the keychain unlocked
// after leaving the app. In the future once keychain stays locked
// when leaving the app the logic here is not needed.
await _keychain.lock();
emit(const GuestSessionState());
} else {
emit(const GuestSessionState());
}
Expand All @@ -56,7 +50,9 @@ final class SessionBloc extends Bloc<SessionEvent, SessionState>
LockSessionEvent event,
Emitter<SessionState> emit,
) async {
await _keychain.lock();
if (await _keychain.hasLock) {
await _keychain.lock();
}
emit(const GuestSessionState());
}

Expand Down Expand Up @@ -88,6 +84,10 @@ final class SessionBloc extends Bloc<SessionEvent, SessionState>
GuestSessionEvent event,
Emitter<SessionState> emit,
) async {
if (await _keychain.hasLock && !await _keychain.isUnlocked) {
await _keychain.unlock(_dummyUnlockFactor);
damian-molinski marked this conversation as resolved.
Show resolved Hide resolved
}

await _keychain.setLockAndBeginWith(
seedPhrase: _dummySeedPhrase,
unlockFactor: _dummyUnlockFactor,
Expand All @@ -101,6 +101,10 @@ final class SessionBloc extends Bloc<SessionEvent, SessionState>
ActiveUserSessionEvent event,
Emitter<SessionState> emit,
) async {
if (await _keychain.hasLock && !await _keychain.isUnlocked) {
await _keychain.unlock(_dummyUnlockFactor);
}

await _keychain.setLockAndBeginWith(
seedPhrase: _dummySeedPhrase,
unlockFactor: _dummyUnlockFactor,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
import 'package:equatable/equatable.dart';

sealed class CryptoException extends Equatable implements Exception {
const CryptoException();

@override
List<Object?> get props => [];
}

/// Usually thrown when trying to decrypt with invalid key
final class CryptoAuthenticationException extends CryptoException {
const CryptoAuthenticationException();

@override
String toString() => 'CryptoAuthenticationException';
}

/// Thrown when data trying to decrypt was tempted with
damian-molinski marked this conversation as resolved.
Show resolved Hide resolved
final class CryptoDataMalformed extends CryptoException {
final String? message;

const CryptoDataMalformed([this.message]);

@override
String toString() {
if (message != null) return 'CryptoDataMalformed: $message';
return 'CryptoDataMalformed';
}
}

final class CryptoVersionUnsupported extends CryptoException {
final String? message;
damian-molinski marked this conversation as resolved.
Show resolved Hide resolved

const CryptoVersionUnsupported([this.message]);

@override
String toString() {
if (message != null) return 'CryptoVersionUnsupported: $message';
return 'CryptoVersionUnsupported';
}
}

final class CryptoAlgorithmUnsupported extends CryptoException {
final String? message;

const CryptoAlgorithmUnsupported([this.message]);

@override
String toString() {
if (message != null) return 'CryptoAlgorithmUnsupported: $message';
return 'CryptoAlgorithmUnsupported';
}
}
Original file line number Diff line number Diff line change
@@ -1,2 +1,4 @@
export 'crypto_exception.dart';
export 'network_error.dart';
export 'secure_storage_error.dart';
export 'vault_exception.dart';
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import 'package:equatable/equatable.dart';

sealed class VaultException extends Equatable implements Exception {
const VaultException();

@override
List<Object?> get props => [];
}

final class LockNotFoundException extends VaultException {
final String? message;
damian-molinski marked this conversation as resolved.
Show resolved Hide resolved

const LockNotFoundException([this.message]);

@override
String toString() {
if (message != null) return 'LockNotFoundException: $message';
return 'LockNotFoundException';
}
}

final class VaultLockedException extends VaultException {
const VaultLockedException();

@override
String toString() => 'VaultLockedException';
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,5 @@ export 'storage/dummy_auth_storage.dart';
export 'storage/secure_storage.dart';
export 'storage/storage.dart';
export 'storage/vault/lock_factor.dart';
export 'storage/vault/lock_factor_codec.dart' show LockFactorCodec;
export 'storage/vault/secure_storage_vault.dart';
export 'storage/vault/vault.dart';
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
import 'package:flutter/foundation.dart';

/// An abstract interface that defines cryptographic operations such as
/// key derivation, encryption, decryption, and key verification.
abstract interface class CryptoService {
damian-molinski marked this conversation as resolved.
Show resolved Hide resolved
/// Derives a cryptographic key from a given seed, with an optional salt.
///
/// The derived key is generated based on the provided [seed], which serves
/// as the primary input. Optionally, a [salt] can be used to further
/// randomize the key derivation process, increasing security.
///
/// - [seed]: The main input data used for key derivation.
/// - [salt]: Optional salt value to randomize the derived key (can be null).
///
/// Returns a [Future] that completes with the derived key as a [Uint8List].
Future<Uint8List> deriveKey(
Uint8List seed, {
Uint8List? salt,
});

/// Verifies if a given cryptographic key is correctly derived from a seed.
///
/// This method checks whether the provided [key] matches the key that would
/// be derived from the given [seed]. This can be useful to verify integrity
/// or correctness of the key derivation process.
///
/// - [seed]: The input data used for key derivation.
/// - [key]: The derived key that needs to be verified.
///
/// Returns a [Future] that completes with `true` if the [key] is valid and
/// correctly derived from the [seed], or `false` otherwise.
Future<bool> verifyKey(
Uint8List seed, {
required Uint8List key,
});

/// Decrypts the provided [data] using the specified cryptographic [key],
/// usually build using [deriveKey].
///
/// This method takes encrypted [data] and decrypts it using the provided
/// [key]. The decryption algorithm and the format of the data should be
/// defined by the implementing class.
///
/// - [data]: The encrypted data to be decrypted.
/// - [key]: The key used for decryption.
///
/// Returns a [Future] that completes with the decrypted data as a
/// [Uint8List].
Future<Uint8List> decrypt(
Uint8List data, {
required Uint8List key,
});

/// Encrypts the provided [data] using the specified cryptographic [key],
/// usually build using [deriveKey].
///
/// This method takes plaintext [data] and encrypts it using the provided
/// [key]. The encryption algorithm and format of the output should be defined
/// by the implementing class.
///
/// - [data]: The plaintext data to be encrypted.
/// - [key]: The key used for encryption.
///
/// Returns a [Future] that completes with the encrypted data as a
/// [Uint8List].
Future<Uint8List> encrypt(
Uint8List data, {
required Uint8List key,
});
}
Loading
Loading