Fix OutOfMemoryError on large files
This commit is contained in:
parent
4d0dd7e5e3
commit
5da95a0c39
@ -1,4 +1,4 @@
|
|||||||
// Copyright (c) 2020
|
// Copyright (c) 2021
|
||||||
// Author: Hugo Pointcheval
|
// Author: Hugo Pointcheval
|
||||||
|
|
||||||
import 'dart:typed_data';
|
import 'dart:typed_data';
|
||||||
@ -52,14 +52,23 @@ abstract class Cipher {
|
|||||||
///
|
///
|
||||||
/// It's the result of an encryption.
|
/// It's the result of an encryption.
|
||||||
abstract class CipherText {
|
abstract class CipherText {
|
||||||
/// Returns the standard algorithm name used for this ciphertext
|
/// Returns the standard algorithm name used for this ciphertext.
|
||||||
CipherAlgorithm get algorithm;
|
CipherAlgorithm get algorithm;
|
||||||
|
|
||||||
/// Returns the data of this ciphertext
|
/// Returns the data of this ciphertext (in chunks).
|
||||||
Uint8List get bytes;
|
List<Uint8List> get bytes;
|
||||||
|
|
||||||
/// Returns the IV of this cipertext
|
/// Returns the IV of this cipertext (in chunks).
|
||||||
Uint8List get iv;
|
List<Uint8List> get iv;
|
||||||
|
|
||||||
|
/// Returns the chunk number of this cipherText.
|
||||||
|
int get size;
|
||||||
|
|
||||||
|
/// Returns this ciphertext in simple Byte Array format.
|
||||||
|
Uint8List encode();
|
||||||
|
|
||||||
|
/// Transforms a simple Byte Array to a NativeCrypto cipherText.
|
||||||
|
void decode(Uint8List src);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Represents a pair of [BlockCipherMode] and [Padding]
|
/// Represents a pair of [BlockCipherMode] and [Padding]
|
||||||
|
@ -1,8 +1,10 @@
|
|||||||
// Copyright (c) 2020
|
// Copyright (c) 2021
|
||||||
// Author: Hugo Pointcheval
|
// Author: Hugo Pointcheval
|
||||||
|
|
||||||
import 'dart:typed_data';
|
import 'dart:typed_data';
|
||||||
|
|
||||||
|
import 'package:native_crypto/native_crypto.dart';
|
||||||
|
|
||||||
import '../cipher.dart';
|
import '../cipher.dart';
|
||||||
import '../exceptions.dart';
|
import '../exceptions.dart';
|
||||||
import '../key.dart';
|
import '../key.dart';
|
||||||
@ -81,9 +83,28 @@ class AESCipher implements Cipher {
|
|||||||
} else if (_sk == null || _sk.isEmpty) {
|
} else if (_sk == null || _sk.isEmpty) {
|
||||||
throw CipherInitException('Invalid key size.');
|
throw CipherInitException('Invalid key size.');
|
||||||
}
|
}
|
||||||
List<Uint8List> c =
|
Uint8List dataToEncrypt;
|
||||||
await Platform().encrypt(data, _sk.encoded, algorithm, _params);
|
int maxSize = 33554432;
|
||||||
return AESCipherText(c[0], c[1]);
|
AESCipherText cipherText = AESCipherText.empty();
|
||||||
|
// If data is bigger than 32mB -> split in chunks
|
||||||
|
if (data.length > maxSize) {
|
||||||
|
int chunkNb = (data.length / maxSize).ceil();
|
||||||
|
for (var i = 0; i < chunkNb; i++) {
|
||||||
|
if (i < (chunkNb - 1)) {
|
||||||
|
dataToEncrypt = data.sublist(i * maxSize, (i + 1) * maxSize);
|
||||||
|
} else {
|
||||||
|
dataToEncrypt = data.sublist(i * maxSize);
|
||||||
|
}
|
||||||
|
List<Uint8List> c = await Platform()
|
||||||
|
.encrypt(dataToEncrypt, _sk.encoded, algorithm, _params);
|
||||||
|
cipherText.append(c[0], c[1]);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
List<Uint8List> c =
|
||||||
|
await Platform().encrypt(data, _sk.encoded, algorithm, _params);
|
||||||
|
cipherText.append(c[0], c[1]);
|
||||||
|
}
|
||||||
|
return cipherText;
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@ -96,29 +117,106 @@ class AESCipher implements Cipher {
|
|||||||
throw CipherInitException('Cipher not properly initialized.');
|
throw CipherInitException('Cipher not properly initialized.');
|
||||||
} else if (_sk == null || _sk.isEmpty) {
|
} else if (_sk == null || _sk.isEmpty) {
|
||||||
throw CipherInitException('Invalid key size.');
|
throw CipherInitException('Invalid key size.');
|
||||||
|
} else if (cipherText.bytes.length != cipherText.iv.length) {
|
||||||
|
throw DecryptionException(
|
||||||
|
"This cipher text's bytes chunks length is not the same as iv chunks length");
|
||||||
}
|
}
|
||||||
List<Uint8List> payload = [cipherText.bytes, cipherText.iv];
|
|
||||||
Uint8List d =
|
BytesBuilder decryptedData = BytesBuilder();
|
||||||
await Platform().decrypt(payload, _sk.encoded, algorithm, _params);
|
if (cipherText.size > 1) {
|
||||||
return d;
|
for (var i = 0; i < cipherText.size; i++) {
|
||||||
|
List<Uint8List> payload = [cipherText.bytes[i], cipherText.iv[i]];
|
||||||
|
Uint8List d =
|
||||||
|
await Platform().decrypt(payload, _sk.encoded, algorithm, _params);
|
||||||
|
decryptedData.add(d);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
List<Uint8List> payload = [cipherText.bytes[0], cipherText.iv[0]];
|
||||||
|
Uint8List d =
|
||||||
|
await Platform().decrypt(payload, _sk.encoded, algorithm, _params);
|
||||||
|
decryptedData.add(d);
|
||||||
|
}
|
||||||
|
return decryptedData.toBytes();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class AESCipherText implements CipherText {
|
class AESCipherText implements CipherText {
|
||||||
Uint8List _bytes;
|
List<Uint8List> _bytes;
|
||||||
Uint8List _iv;
|
List<Uint8List> _iv;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
CipherAlgorithm get algorithm => CipherAlgorithm.AES;
|
CipherAlgorithm get algorithm => CipherAlgorithm.AES;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Uint8List get bytes => _bytes;
|
List<Uint8List> get bytes => _bytes;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Uint8List get iv => _iv;
|
List<Uint8List> get iv => _iv;
|
||||||
|
|
||||||
|
@override
|
||||||
|
int get size => _bytes.length;
|
||||||
|
|
||||||
AESCipherText(Uint8List bytes, Uint8List iv) {
|
AESCipherText(Uint8List bytes, Uint8List iv) {
|
||||||
|
_bytes = List.from([bytes]);
|
||||||
|
_iv = List.from([iv]);
|
||||||
|
}
|
||||||
|
|
||||||
|
AESCipherText.from(List<Uint8List> bytes, List<Uint8List> iv) {
|
||||||
_bytes = bytes;
|
_bytes = bytes;
|
||||||
_iv = iv;
|
_iv = iv;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
AESCipherText.empty() {
|
||||||
|
_bytes = <Uint8List>[];
|
||||||
|
_iv = <Uint8List>[];
|
||||||
|
}
|
||||||
|
|
||||||
|
void append(Uint8List bytes, Uint8List iv) {
|
||||||
|
_bytes.add(bytes);
|
||||||
|
_iv.add(iv);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns this ciphertext in [Uint8List] format.
|
||||||
|
///
|
||||||
|
/// Encoding
|
||||||
|
/// --------
|
||||||
|
/// Uint8List encoding is : IV_1 + M_1 + IV_2 + M_2 + ... + IV_n + M_n
|
||||||
|
///
|
||||||
|
/// Where **IV_k** is the IV of the cipher text **M_k**
|
||||||
|
///
|
||||||
|
/// IV is **always** 16 bytes long, And the **M** are all max
|
||||||
|
/// size (of 33 554 480 bytes) except the last one which is shorter than the others.
|
||||||
|
Uint8List encode() {
|
||||||
|
BytesBuilder builder = BytesBuilder();
|
||||||
|
for (var i = 0; i < size; i++) {
|
||||||
|
builder.add(_iv[i]);
|
||||||
|
builder.add(_bytes[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
return builder.toBytes();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Transforms a [Uint8List] to a *NativeCrypto* cipherText.
|
||||||
|
///
|
||||||
|
/// Decoding
|
||||||
|
/// --------
|
||||||
|
/// See the list as a chain of chunks (IV and Messages)
|
||||||
|
/// `[IV][MESSAGE][IV][MESSAGE] ... [IV][MESSA...]`
|
||||||
|
///
|
||||||
|
/// Chunk length is IV length + Message length = 16 + 33 554 480 bytes
|
||||||
|
void decode(Uint8List src) {
|
||||||
|
ByteBuffer buffer = src.buffer;
|
||||||
|
|
||||||
|
int chunkSize = 16 + 33554480;
|
||||||
|
int chunkNb = (buffer.lengthInBytes / chunkSize).ceil();
|
||||||
|
|
||||||
|
for (var i = 0; i < chunkNb; i++) {
|
||||||
|
_iv.add(buffer.asUint8List(i * chunkSize, 16));
|
||||||
|
if (i < (chunkNb - 1)) {
|
||||||
|
_bytes.add(buffer.asUint8List(16 + i * chunkSize, 33554480));
|
||||||
|
} else {
|
||||||
|
_bytes.add(buffer.asUint8List(16 + i * chunkSize));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user