fix: update code to pass all tests

This commit is contained in:
Hugo Pointcheval 2022-05-26 23:23:13 +02:00
parent 96f9aad1b3
commit ebdcf00c15
Signed by: hugo
GPG Key ID: A9E8E9615379254F
11 changed files with 174 additions and 74 deletions

View File

@ -3,7 +3,7 @@
// ----- // -----
// File: cipher_page.dart // File: cipher_page.dart
// Created Date: 28/12/2021 13:33:15 // Created Date: 28/12/2021 13:33:15
// Last Modified: 26/05/2022 20:39:37 // Last Modified: 26/05/2022 21:07:54
// ----- // -----
// Copyright (c) 2021 // Copyright (c) 2021
@ -26,7 +26,8 @@ class CipherPage extends ConsumerWidget {
final Output encryptionStatus = Output(); final Output encryptionStatus = Output();
final Output decryptionStatus = Output(); final Output decryptionStatus = Output();
final TextEditingController _plainTextController = TextEditingController()..text = 'PlainText'; final TextEditingController _plainTextController = TextEditingController()
..text = 'PlainText';
CipherTextWrapper? cipherText; CipherTextWrapper? cipherText;
Future<void> _encrypt(WidgetRef ref, Cipher cipher) async { Future<void> _encrypt(WidgetRef ref, Cipher cipher) async {
@ -58,9 +59,10 @@ class CipherPage extends ConsumerWidget {
// Recreate cipher text with altered data // Recreate cipher text with altered data
cipherText = CipherTextWrapper.fromBytes( cipherText = CipherTextWrapper.fromBytes(
_altered, _altered,
12, ivLength: AESMode.gcm.ivLength,
_altered.length - 28, messageLength:
16, _altered.length - (AESMode.gcm.ivLength + AESMode.gcm.tagLength),
tagLength: AESMode.gcm.tagLength,
); );
encryptionStatus.print('String successfully encrypted:\n'); encryptionStatus.print('String successfully encrypted:\n');

View File

@ -3,7 +3,7 @@
// ----- // -----
// File: kdf_page.dart // File: kdf_page.dart
// Created Date: 28/12/2021 13:40:34 // Created Date: 28/12/2021 13:40:34
// Last Modified: 26/05/2022 20:30:31 // Last Modified: 26/05/2022 21:09:47
// ----- // -----
// Copyright (c) 2021 // Copyright (c) 2021
@ -26,8 +26,10 @@ class KdfPage extends ConsumerWidget {
final Output pbkdf2Status = Output(); final Output pbkdf2Status = Output();
final Output hashStatus = Output(large: true); final Output hashStatus = Output(large: true);
final TextEditingController _pwdTextController = TextEditingController()..text = 'Password'; final TextEditingController _pwdTextController = TextEditingController()
final TextEditingController _messageTextController = TextEditingController()..text = 'Message'; ..text = 'Password';
final TextEditingController _messageTextController = TextEditingController()
..text = 'Message';
Future<void> _generate(WidgetRef ref) async { Future<void> _generate(WidgetRef ref) async {
Session state = ref.read(sessionProvider.state).state; Session state = ref.read(sessionProvider.state).state;
@ -50,7 +52,11 @@ class KdfPage extends ConsumerWidget {
if (password.isEmpty) { if (password.isEmpty) {
pbkdf2Status.print('Password is empty'); pbkdf2Status.print('Password is empty');
} else { } else {
Pbkdf2 _pbkdf2 = Pbkdf2(32, 1000, algorithm: HashAlgorithm.sha512); Pbkdf2 _pbkdf2 = Pbkdf2(
keyBytesCount: 32,
iterations: 1000,
algorithm: HashAlgorithm.sha512,
);
SecretKey sk = await _pbkdf2.derive(password: password, salt: 'salt'); SecretKey sk = await _pbkdf2.derive(password: password, salt: 'salt');
state.setKey(sk); state.setKey(sk);
pbkdf2Status.print('Key successfully derived.'); pbkdf2Status.print('Key successfully derived.');

View File

@ -3,7 +3,7 @@
// ----- // -----
// File: aes.dart // File: aes.dart
// Created Date: 16/12/2021 16:28:00 // Created Date: 16/12/2021 16:28:00
// Last Modified: 26/05/2022 19:43:22 // Last Modified: 26/05/2022 21:07:01
// ----- // -----
// Copyright (c) 2022 // Copyright (c) 2022
@ -21,6 +21,10 @@ import 'package:native_crypto/src/utils/cipher_algorithm.dart';
import 'package:native_crypto/src/utils/extensions.dart'; import 'package:native_crypto/src/utils/extensions.dart';
import 'package:native_crypto_platform_interface/native_crypto_platform_interface.dart'; import 'package:native_crypto_platform_interface/native_crypto_platform_interface.dart';
export 'aes_key_size.dart';
export 'aes_mode.dart';
export 'aes_padding.dart';
/// An AES cipher. /// An AES cipher.
/// ///
/// [AES] is a [Cipher] that can be used to encrypt or decrypt data. /// [AES] is a [Cipher] that can be used to encrypt or decrypt data.
@ -94,11 +98,11 @@ class AES implements Cipher {
); );
} else { } else {
return CipherText.fromBytes( return CipherText.fromBytes(
12,
encrypted.length - 28,
16,
CipherAlgorithm.aes,
encrypted, encrypted,
ivLength: 12,
messageLength: encrypted.length - 28,
tagLength: 16,
cipherAlgorithm: CipherAlgorithm.aes,
); );
} }
} }

View File

@ -3,7 +3,7 @@
// ----- // -----
// File: aes_mode.dart // File: aes_mode.dart
// Created Date: 23/05/2022 22:09:16 // Created Date: 23/05/2022 22:09:16
// Last Modified: 26/05/2022 18:41:31 // Last Modified: 26/05/2022 21:03:26
// ----- // -----
// Copyright (c) 2022 // Copyright (c) 2022
@ -11,10 +11,20 @@ import 'package:native_crypto/src/ciphers/aes/aes_padding.dart';
/// Defines the AES modes of operation. /// Defines the AES modes of operation.
enum AESMode { enum AESMode {
gcm([AESPadding.none]); gcm([AESPadding.none], 12, 16);
/// Returns the list of supported [AESPadding] for this [AESMode]. /// Returns the list of supported [AESPadding] for this [AESMode].
final List<AESPadding> supportedPaddings; final List<AESPadding> supportedPaddings;
const AESMode(this.supportedPaddings); /// Returns the default IV length for this [AESMode].
final int ivLength;
/// Returns the default tag length for this [AESMode].
final int tagLength;
const AESMode(
this.supportedPaddings, [
this.ivLength = 16,
this.tagLength = 0,
]);
} }

View File

@ -3,7 +3,7 @@
// ----- // -----
// File: cipher_text.dart // File: cipher_text.dart
// Created Date: 16/12/2021 16:59:53 // Created Date: 16/12/2021 16:59:53
// Last Modified: 26/05/2022 19:43:57 // Last Modified: 26/05/2022 22:20:40
// ----- // -----
// Copyright (c) 2021 // Copyright (c) 2021
@ -45,16 +45,33 @@ class CipherText extends ByteArray {
); );
factory CipherText.fromBytes( factory CipherText.fromBytes(
int ivLength, Uint8List bytes, {
int messageLength, required int ivLength,
int tagLength, required int messageLength,
required int tagLength,
CipherAlgorithm? cipherAlgorithm, CipherAlgorithm? cipherAlgorithm,
Uint8List bytes, }) {
) { if (ivLength.isNegative ||
messageLength.isNegative ||
tagLength.isNegative) {
throw NativeCryptoException(
message: 'Invalid length! Must be positive.',
code: NativeCryptoExceptionCode.invalid_argument.code,
);
}
if (bytes.isEmpty) {
throw NativeCryptoException(
message: 'Passed data is empty!',
code: NativeCryptoExceptionCode.invalid_argument.code,
);
}
if (bytes.length != ivLength + messageLength + tagLength) { if (bytes.length != ivLength + messageLength + tagLength) {
throw NativeCryptoException( throw NativeCryptoException(
message: 'Invalid cipher text length! ' message: 'Invalid cipher text length! '
'Expected: ${ivLength + messageLength + tagLength} bytes', 'Expected: ${ivLength + messageLength + tagLength} bytes '
'got: ${bytes.length} bytes.',
code: NativeCryptoExceptionCode.invalid_argument.code, code: NativeCryptoExceptionCode.invalid_argument.code,
); );
} }

View File

@ -3,7 +3,7 @@
// ----- // -----
// File: cipher_text_wrapper.dart // File: cipher_text_wrapper.dart
// Created Date: 26/05/2022 14:27:32 // Created Date: 26/05/2022 14:27:32
// Last Modified: 26/05/2022 20:32:38 // Last Modified: 26/05/2022 22:11:42
// ----- // -----
// Copyright (c) 2022 // Copyright (c) 2022
@ -49,10 +49,10 @@ class CipherTextWrapper {
/// [NativeCryptoExceptionCode.invalid_argument] if the [Uint8List] is /// [NativeCryptoExceptionCode.invalid_argument] if the [Uint8List] is
/// not a valid [CipherText] or a [List] of [CipherText]. /// not a valid [CipherText] or a [List] of [CipherText].
factory CipherTextWrapper.fromBytes( factory CipherTextWrapper.fromBytes(
Uint8List bytes, Uint8List bytes, {
int ivLength, required int ivLength,
int messageLength, required int messageLength,
int tagLength, { required int tagLength,
CipherAlgorithm? cipherAlgorithm, CipherAlgorithm? cipherAlgorithm,
int? chunkSize, int? chunkSize,
}) { }) {
@ -62,11 +62,11 @@ class CipherTextWrapper {
if (bytes.length <= chunkSize) { if (bytes.length <= chunkSize) {
return CipherTextWrapper.single( return CipherTextWrapper.single(
CipherText.fromBytes( CipherText.fromBytes(
ivLength,
messageLength,
tagLength,
cipherAlgorithm,
bytes, bytes,
ivLength: ivLength,
messageLength: messageLength,
tagLength: tagLength,
cipherAlgorithm: cipherAlgorithm,
), ),
); );
} else { } else {
@ -75,11 +75,11 @@ class CipherTextWrapper {
final chunk = bytes.sublist(i, i + chunkSize); final chunk = bytes.sublist(i, i + chunkSize);
cipherTexts.add( cipherTexts.add(
CipherText.fromBytes( CipherText.fromBytes(
ivLength,
messageLength,
tagLength,
cipherAlgorithm,
chunk, chunk,
ivLength: ivLength,
messageLength: messageLength,
tagLength: tagLength,
cipherAlgorithm: cipherAlgorithm,
), ),
); );
} }
@ -128,7 +128,7 @@ class CipherTextWrapper {
if (isSingle) { if (isSingle) {
return single.bytes; return single.bytes;
} else { } else {
return list.map((cipherText) => cipherText.bytes).toList().sum(); return list.map((cipherText) => cipherText.bytes).toList().combine();
} }
} }

View File

@ -3,7 +3,7 @@
// ----- // -----
// File: cipher.dart // File: cipher.dart
// Created Date: 16/12/2021 16:28:00 // Created Date: 16/12/2021 16:28:00
// Last Modified: 26/05/2022 17:38:26 // Last Modified: 26/05/2022 21:21:07
// ----- // -----
// Copyright (c) 2021 // Copyright (c) 2021
@ -20,7 +20,11 @@ import 'package:native_crypto/src/utils/cipher_algorithm.dart';
/// ///
/// This interface is implemented by all the ciphers in NativeCrypto. /// This interface is implemented by all the ciphers in NativeCrypto.
abstract class Cipher { abstract class Cipher {
static int _bytesCountPerChunk = 33554432; static const int _bytesCountPerChunkDefault = 33554432;
static int _bytesCountPerChunk = _bytesCountPerChunkDefault;
/// Returns the default number of bytes per chunk.
static int get defaultBytesCountPerChunk => _bytesCountPerChunkDefault;
/// Returns the size of a chunk of data /// Returns the size of a chunk of data
/// that can be processed by the [Cipher]. /// that can be processed by the [Cipher].

View File

@ -3,7 +3,7 @@
// ----- // -----
// File: pbkdf2.dart // File: pbkdf2.dart
// Created Date: 17/12/2021 14:50:42 // Created Date: 17/12/2021 14:50:42
// Last Modified: 26/05/2022 18:51:59 // Last Modified: 26/05/2022 23:19:46
// ----- // -----
// Copyright (c) 2021 // Copyright (c) 2021
@ -29,31 +29,64 @@ class Pbkdf2 extends KeyDerivation {
@override @override
KdfAlgorithm get algorithm => KdfAlgorithm.pbkdf2; KdfAlgorithm get algorithm => KdfAlgorithm.pbkdf2;
Pbkdf2( Pbkdf2({
int keyBytesCount, required int keyBytesCount,
int iterations, { required int iterations,
HashAlgorithm algorithm = HashAlgorithm.sha256, HashAlgorithm algorithm = HashAlgorithm.sha256,
}) : _keyBytesCount = keyBytesCount, }) : _keyBytesCount = keyBytesCount,
_iterations = iterations, _iterations = iterations,
_hash = algorithm; _hash = algorithm {
if (keyBytesCount < 0) {
@override
Future<SecretKey> derive({String? password, String? salt}) async {
if (password == null || salt == null) {
throw NativeCryptoException( throw NativeCryptoException(
message: 'Password and salt cannot be null. ' message: 'keyBytesCount must be positive.',
'Here is the password: $password, here is the salt: $salt',
code: NativeCryptoExceptionCode.invalid_argument.code, code: NativeCryptoExceptionCode.invalid_argument.code,
); );
} }
final Uint8List? derivation = await platform.pbkdf2( if (iterations <= 0) {
password, throw NativeCryptoException(
salt, message: 'iterations must be strictly positive.',
code: NativeCryptoExceptionCode.invalid_argument.code,
);
}
}
@override
Future<SecretKey> derive({String? password, String? salt}) async {
Uint8List? derivation;
if (_keyBytesCount == 0) {
return SecretKey(Uint8List(0));
}
if (password.isNull) {
throw NativeCryptoException(
message: 'Password cannot be null.',
code: NativeCryptoExceptionCode.invalid_argument.code,
);
}
if (salt.isNull) {
throw NativeCryptoException(
message: 'Salt cannot be null.',
code: NativeCryptoExceptionCode.invalid_argument.code,
);
}
try {
derivation = await platform.pbkdf2(
password!,
salt!,
_keyBytesCount, _keyBytesCount,
_iterations, _iterations,
_hash.name, _hash.name,
); );
} catch (e, s) {
throw NativeCryptoException(
message: '$e',
code: NativeCryptoExceptionCode.platform_throws.code,
stackTrace: s,
);
}
if (derivation.isNull) { if (derivation.isNull) {
throw NativeCryptoException( throw NativeCryptoException(

View File

@ -3,7 +3,7 @@
// ----- // -----
// File: secret_key.dart // File: secret_key.dart
// Created Date: 28/12/2021 13:36:54 // Created Date: 28/12/2021 13:36:54
// Last Modified: 26/05/2022 19:26:35 // Last Modified: 26/05/2022 23:13:10
// ----- // -----
// Copyright (c) 2021 // Copyright (c) 2021
@ -28,6 +28,10 @@ class SecretKey extends BaseKey {
static Future<SecretKey> fromSecureRandom(int bitsCount) async { static Future<SecretKey> fromSecureRandom(int bitsCount) async {
Uint8List? key; Uint8List? key;
if (bitsCount == 0) {
return SecretKey(Uint8List(0));
}
try { try {
key = await platform.generateSecretKey(bitsCount); key = await platform.generateSecretKey(bitsCount);
} catch (e, s) { } catch (e, s) {

View File

@ -3,7 +3,7 @@
// ----- // -----
// File: extensions.dart // File: extensions.dart
// Created Date: 26/05/2022 12:12:48 // Created Date: 26/05/2022 12:12:48
// Last Modified: 26/05/2022 18:52:48 // Last Modified: 26/05/2022 22:15:33
// ----- // -----
// Copyright (c) 2022 // Copyright (c) 2022
@ -30,14 +30,10 @@ extension ListIntX on List<int> {
} }
extension ListUint8ListX on List<Uint8List> { extension ListUint8ListX on List<Uint8List> {
/// Reduce a [List] of [Uint8List] to a [Uint8List]. /// Reduce a [List] of [Uint8List] to a [Uint8List].
Uint8List sum() { Uint8List combine() {
for (var i = 1; i < length; i++) { if (isEmpty) return Uint8List(0);
first.addAll(this[i]); return reduce((value, element) => value.plus(element));
removeAt(i);
}
return first;
} }
} }
@ -90,6 +86,5 @@ extension Uint8ListX on Uint8List {
} }
/// Returns a concatenation of this with the other [Uint8List]. /// Returns a concatenation of this with the other [Uint8List].
Uint8List operator +(final Uint8List other) => Uint8List plus(final Uint8List other) => [...this, ...other].toTypedList();
[...this, ...other].toTypedList();
} }

View File

@ -3,13 +3,15 @@
// ----- // -----
// File: hash_algorithm.dart // File: hash_algorithm.dart
// Created Date: 23/05/2022 22:01:59 // Created Date: 23/05/2022 22:01:59
// Last Modified: 26/05/2022 18:53:38 // Last Modified: 26/05/2022 22:59:04
// ----- // -----
// Copyright (c) 2022 // Copyright (c) 2022
import 'dart:typed_data'; import 'dart:typed_data';
import 'package:native_crypto/src/platform.dart'; import 'package:native_crypto/src/platform.dart';
import 'package:native_crypto/src/utils/extensions.dart';
import 'package:native_crypto_platform_interface/native_crypto_platform_interface.dart';
/// Defines the hash algorithms. /// Defines the hash algorithms.
enum HashAlgorithm { enum HashAlgorithm {
@ -19,7 +21,30 @@ enum HashAlgorithm {
/// Digest the [data] using this [HashAlgorithm]. /// Digest the [data] using this [HashAlgorithm].
Future<Uint8List> digest(Uint8List data) async { Future<Uint8List> digest(Uint8List data) async {
final Uint8List hash = (await platform.digest(data, name)) ?? Uint8List(0); Uint8List? hash;
try {
hash = await platform.digest(data, name);
} catch (e, s) {
throw NativeCryptoException(
message: '$e',
code: NativeCryptoExceptionCode.platform_throws.code,
stackTrace: s,
);
}
if (hash.isNull) {
throw NativeCryptoException(
message: 'Failed to digest data! Platform returned null.',
code: NativeCryptoExceptionCode.platform_returned_null.code,
);
}
if (hash!.isEmpty) {
throw NativeCryptoException(
message: 'Failed to digest data! Platform returned no data.',
code: NativeCryptoExceptionCode.platform_returned_empty_data.code,
);
}
return hash; return hash;
} }