From 3a42d25b0c356606c26a238384b9f2189572d954 Mon Sep 17 00:00:00 2001 From: Tim Maffett Date: Tue, 5 Apr 2022 05:03:51 -0700 Subject: [PATCH] fix .fromLength() to be cryptographically secure, as I am sure any user would assume. fixes #246 (#266) * remove IV from fernet example, it is not used for fernet (and was not used in the example) * fix fromLength() to be secure, add allZerosOfLength() for use where uniform 0's where needed * added docs to IV and Key object * added docs on primary contructors also --- lib/src/encrypted.dart | 55 +++++++++++++++++++++++++++++++++++++++- test/battle_test.dart | 4 +-- test/encrypt_test.dart | 18 ++++++------- test/encrypted_test.dart | 21 +++++++++++++-- 4 files changed, 84 insertions(+), 14 deletions(-) diff --git a/lib/src/encrypted.dart b/lib/src/encrypted.dart index 11e6a90..d728f09 100644 --- a/lib/src/encrypted.dart +++ b/lib/src/encrypted.dart @@ -2,6 +2,8 @@ part of encrypt; /// Represents an encripted value. class Encrypted { + + /// Creates an Encrypted object from a Uint8List. Encrypted(this._bytes); final Uint8List _bytes; @@ -21,7 +23,18 @@ class Encrypted { : _bytes = Uint8List.fromList(convert.utf8.encode(input)); /// Creates an Encrypted object from a length. - Encrypted.fromLength(int length) : _bytes = Uint8List(length); + /// The key is filled with [length] bytes generated by a + /// Random.secure() generator + Encrypted.fromLength(int length) : _bytes = SecureRandom(length).bytes; + + /// Creates an Encrypted object from a length. + /// The key is filled with [length] bytes generated by a + /// Random.secure() generator + Encrypted.fromSecureRandom(int length) : _bytes = SecureRandom(length).bytes; + + /// Creates an Encrypted object of ALL ZEROS from a length. + /// The key is ALL ZEROS - NOT CRYPTOGRAPHICALLY SECURE! + Encrypted.allZerosOfLength(int length) : _bytes = Uint8List(length); /// Gets the Encrypted bytes. Uint8List get bytes => _bytes; @@ -45,23 +58,63 @@ class Encrypted { /// Represents an Initialization Vector. class IV extends Encrypted { + + /// Creates an Initialization Vector object from a Uint8List. IV(Uint8List bytes) : super(bytes); + + /// Creates an Initialization Vector object from a hexdecimal string. IV.fromBase16(String encoded) : super.fromBase16(encoded); + + /// Creates an Initialization Vector object from a Base64 string. IV.fromBase64(String encoded) : super.fromBase64(encoded); + + /// Creates an Initialization Vector object from a UTF-8 string. IV.fromUtf8(String input) : super.fromUtf8(input); + + /// Creates an Initialization Vector object from a length. + /// The key is filled with [length] bytes generated by a + /// Random.secure() generator IV.fromLength(int length) : super.fromLength(length); + + /// Creates an Initialization Vector object from a length. + /// The key is filled with [length] bytes generated by a + /// Random.secure() generator IV.fromSecureRandom(int length) : super(SecureRandom(length).bytes); + + /// Creates an Initialization Vector object of ALL ZEROS from a length. + /// The key is ALL ZEROS - NOT CRYPTOGRAPHICALLY SECURE! + IV.allZerosOfLength(int length) : super.allZerosOfLength(length); } /// Represents an Encryption Key. class Key extends Encrypted { + + /// Creates an Encryption Key object from a Uint8List. Key(Uint8List bytes) : super(bytes); + + /// Creates an Encryption Key object from a hexdecimal string. Key.fromBase16(String encoded) : super.fromBase16(encoded); + + /// Creates an Encryption Key object from a Base64 string. Key.fromBase64(String encoded) : super.fromBase64(encoded); + + /// Creates an Encryption Key object from a UTF-8 string. Key.fromUtf8(String input) : super.fromUtf8(input); + + /// Creates an Encryption Key object from a length. + /// The key is filled with [length] bytes generated by a + /// Random.secure() generator Key.fromLength(int length) : super.fromLength(length); + + /// Creates an Encryption Key object from a length. + /// The key is filled with [length] bytes generated by a + /// Random.secure() generator Key.fromSecureRandom(int length) : super(SecureRandom(length).bytes); + /// Creates an Encryption Key object of ALL ZEROS from a length. + /// The key is ALL ZEROS - NOT CRYPTOGRAPHICALLY SECURE! + Key.allZerosOfLength(int length) : super.allZerosOfLength(length); + Key stretch(int desiredKeyLength, {int iterationCount = 100, Uint8List? salt}) { if (salt == null) { diff --git a/test/battle_test.dart b/test/battle_test.dart index e207483..a1d3c9a 100644 --- a/test/battle_test.dart +++ b/test/battle_test.dart @@ -5,12 +5,12 @@ void main() { group('Battle test', () { test('Emoji', () { const encoded = 'iPC4DII05qnIJsFm6/RUp6OQEnvLSTq1pW+4/cjHf4c='; - final encrypter = Encrypter(AES(Key.fromLength(32))); + final encrypter = Encrypter(AES(Key.allZerosOfLength(32))); expect( Encrypted.fromBase64(encoded), equals( - encrypter.encrypt('Text to encrypt 😀', iv: IV.fromLength(16)))); + encrypter.encrypt('Text to encrypt 😀', iv: IV.allZerosOfLength(16)))); }); }); } diff --git a/test/encrypt_test.dart b/test/encrypt_test.dart index 2dfd08a..863934e 100644 --- a/test/encrypt_test.dart +++ b/test/encrypt_test.dart @@ -23,7 +23,7 @@ void main() { final encrypter = Encrypter(fernet); final encrypted = Encrypted.fromBase64( 'gAAAAABdSZ/GAAAAAAAAAAAAAAAAAAAAACxNe+/PVLJMTKmBdPrlHat3Bj32TYdt1EKCz2jlJykTrwtMgSuZdLGXAIkmResqHLA5g0k7kzOCdHe02noK7YmV75oA2sLjSTE1zao/jtEdEB/aebAOYKQW8ZEm33oyXA=='); - final iv = IV.fromLength(16); + final iv = IV.allZerosOfLength(16); test('encrypt', () { expect(encrypter.encrypt(text, iv: iv), equals(encrypted)); @@ -59,12 +59,12 @@ void main() { final encrypted = Encrypted(base64.decode(encoded)); test('encrypt', () { - expect(encrypter.encrypt(text, iv: IV.fromLength(16)), + expect(encrypter.encrypt(text, iv: IV.allZerosOfLength(16)), equals(encrypted)); }); test('decrypt', () { - expect(encrypter.decrypt(encrypted, iv: IV.fromLength(16)), + expect(encrypter.decrypt(encrypted, iv: IV.allZerosOfLength(16)), equals(text)); }); }); @@ -89,12 +89,12 @@ void main() { final encrypted = Encrypted(base64.decode(encoded)); test('encrypt', () { - expect(encrypter.encrypt(text.padRight(64), iv: IV.fromLength(16)), + expect(encrypter.encrypt(text.padRight(64), iv: IV.allZerosOfLength(16)), equals(encrypted)); }); test('decrypt', () { - expect(encrypter.decrypt(encrypted, iv: IV.fromLength(16)), + expect(encrypter.decrypt(encrypted, iv: IV.allZerosOfLength(16)), equals(text.padRight(64))); }); }); @@ -112,12 +112,12 @@ void main() { final encrypted = Encrypted(base64.decode(encoded)); test('encrypt', () { - expect(encrypter.encrypt(text, iv: IV.fromLength(16)), + expect(encrypter.encrypt(text, iv: IV.allZerosOfLength(16)), equals(encrypted)); }); test('decrypt', () { - expect(encrypter.decrypt(encrypted, iv: IV.fromLength(16)), + expect(encrypter.decrypt(encrypted, iv: IV.allZerosOfLength(16)), equals(text)); }); }); @@ -135,12 +135,12 @@ void main() { test( 'encrypt', () => expect( - encrypter.encrypt(text, iv: IV.fromLength(8)), equals(encrypted))); + encrypter.encrypt(text, iv: IV.allZerosOfLength(8)), equals(encrypted))); test( 'decrypt', () => expect( - encrypter.decrypt(encrypted, iv: IV.fromLength(8)), equals(text))); + encrypter.decrypt(encrypted, iv: IV.allZerosOfLength(8)), equals(text))); }); group('RSA', () { diff --git a/test/encrypted_test.dart b/test/encrypted_test.dart index 00f6a9b..ef9f441 100644 --- a/test/encrypted_test.dart +++ b/test/encrypted_test.dart @@ -20,11 +20,28 @@ void main() { expect(encrypted.bytes, equals([0, 1, 2])); }); - test('fromLength', () { - final encrypted = Encrypted.fromLength(3); + test('allZerosOfLength', () { + final encrypted = Encrypted.allZerosOfLength(3); + expect(encrypted.bytes.length, equals(3)); expect(encrypted.bytes, equals([0, 0, 0])); }); + test('fromLength', () { + final encrypted = Encrypted.fromLength(20); + final encrypted2 = Encrypted.fromLength(20); + expect(encrypted.bytes.length, equals(20)); + expect(encrypted2.bytes.length, equals(20)); + expect(encrypted.bytes, isNot(equals(encrypted2.bytes))); + }); + + test('fromSecureRandom', () { + final encrypted = Encrypted.fromSecureRandom(20); + final encrypted2 = Encrypted.fromSecureRandom(20); + expect(encrypted.bytes.length, equals(20)); + expect(encrypted2.bytes.length, equals(20)); + expect(encrypted.bytes, isNot(equals(encrypted2.bytes))); + }); + test('fromUtf8', () { final encrypted = Encrypted.fromUtf8('\u0000\u0001\u0002'); expect(encrypted.bytes, equals([0, 1, 2]));