ente/lib/utils/crypto_util.dart

334 lines
9.6 KiB
Dart
Raw Normal View History

// @dart=2.9
2021-07-22 06:51:59 +00:00
import 'dart:io' as io;
2020-08-10 23:47:22 +00:00
import 'dart:typed_data';
2020-08-13 23:04:32 +00:00
import 'package:computer/computer.dart';
import 'package:flutter_sodium/flutter_sodium.dart';
import 'package:logging/logging.dart';
import 'package:photos/models/derived_key_result.dart';
2020-10-06 23:12:02 +00:00
import 'package:photos/models/encryption_result.dart';
2020-08-09 20:39:11 +00:00
2022-07-04 06:02:17 +00:00
const int encryptionChunkSize = 4 * 1024 * 1024;
final int decryptionChunkSize =
encryptionChunkSize + Sodium.cryptoSecretstreamXchacha20poly1305Abytes;
Uint8List cryptoSecretboxEasy(Map<String, dynamic> args) {
return Sodium.cryptoSecretboxEasy(args["source"], args["nonce"], args["key"]);
}
Uint8List cryptoSecretboxOpenEasy(Map<String, dynamic> args) {
return Sodium.cryptoSecretboxOpenEasy(
2022-06-11 08:23:52 +00:00
args["cipher"],
args["nonce"],
args["key"],
);
}
Uint8List cryptoPwHash(Map<String, dynamic> args) {
return Sodium.cryptoPwhash(
Sodium.cryptoSecretboxKeybytes,
args["password"],
args["salt"],
args["opsLimit"],
args["memLimit"],
Sodium.cryptoPwhashAlgDefault,
);
}
2021-08-18 12:07:50 +00:00
Uint8List cryptoGenericHash(Map<String, dynamic> args) {
final sourceFile = io.File(args["sourceFilePath"]);
final sourceFileLength = sourceFile.lengthSync();
final inputFile = sourceFile.openSync(mode: io.FileMode.read);
final state =
Sodium.cryptoGenerichashInit(null, Sodium.cryptoGenerichashBytesMax);
var bytesRead = 0;
bool isDone = false;
while (!isDone) {
var chunkSize = encryptionChunkSize;
if (bytesRead + chunkSize >= sourceFileLength) {
chunkSize = sourceFileLength - bytesRead;
isDone = true;
}
final buffer = inputFile.readSync(chunkSize);
bytesRead += chunkSize;
Sodium.cryptoGenerichashUpdate(state, buffer);
}
inputFile.closeSync();
return Sodium.cryptoGenerichashFinal(state, Sodium.cryptoGenerichashBytesMax);
}
2021-05-04 15:32:23 +00:00
EncryptionResult chachaEncryptData(Map<String, dynamic> args) {
final initPushResult =
Sodium.cryptoSecretstreamXchacha20poly1305InitPush(args["key"]);
final encryptedData = Sodium.cryptoSecretstreamXchacha20poly1305Push(
2022-06-11 08:23:52 +00:00
initPushResult.state,
args["source"],
null,
Sodium.cryptoSecretstreamXchacha20poly1305TagFinal,
);
2021-05-04 15:32:23 +00:00
return EncryptionResult(
2022-06-11 08:23:52 +00:00
encryptedData: encryptedData,
header: initPushResult.header,
);
2021-05-04 15:32:23 +00:00
}
2021-08-09 06:26:11 +00:00
Future<EncryptionResult> chachaEncryptFile(Map<String, dynamic> args) async {
final encryptionStartTime = DateTime.now().millisecondsSinceEpoch;
final logger = Logger("ChaChaEncrypt");
final sourceFile = io.File(args["sourceFilePath"]);
final destinationFile = io.File(args["destinationFilePath"]);
2021-08-09 06:33:47 +00:00
final sourceFileLength = await sourceFile.length();
logger.info("Encrypting file of size " + sourceFileLength.toString());
final inputFile = sourceFile.openSync(mode: io.FileMode.read);
final key = args["key"] ?? Sodium.cryptoSecretstreamXchacha20poly1305Keygen();
final initPushResult =
Sodium.cryptoSecretstreamXchacha20poly1305InitPush(key);
var bytesRead = 0;
var tag = Sodium.cryptoSecretstreamXchacha20poly1305TagMessage;
while (tag != Sodium.cryptoSecretstreamXchacha20poly1305TagFinal) {
var chunkSize = encryptionChunkSize;
if (bytesRead + chunkSize >= sourceFileLength) {
chunkSize = sourceFileLength - bytesRead;
tag = Sodium.cryptoSecretstreamXchacha20poly1305TagFinal;
}
final buffer = inputFile.readSync(chunkSize);
bytesRead += chunkSize;
final encryptedData = Sodium.cryptoSecretstreamXchacha20poly1305Push(
2022-06-11 08:23:52 +00:00
initPushResult.state,
buffer,
null,
tag,
);
2021-08-09 06:26:11 +00:00
await destinationFile.writeAsBytes(encryptedData, mode: io.FileMode.append);
}
inputFile.closeSync();
2022-06-11 08:23:52 +00:00
logger.info(
"Encryption time: " +
(DateTime.now().millisecondsSinceEpoch - encryptionStartTime)
.toString(),
);
2020-10-06 23:17:53 +00:00
return EncryptionResult(key: key, header: initPushResult.header);
}
2021-08-09 06:26:11 +00:00
Future<void> chachaDecryptFile(Map<String, dynamic> args) async {
final logger = Logger("ChaChaDecrypt");
final decryptionStartTime = DateTime.now().millisecondsSinceEpoch;
final sourceFile = io.File(args["sourceFilePath"]);
final destinationFile = io.File(args["destinationFilePath"]);
2021-08-09 06:33:47 +00:00
final sourceFileLength = await sourceFile.length();
logger.info("Decrypting file of size " + sourceFileLength.toString());
final inputFile = sourceFile.openSync(mode: io.FileMode.read);
final pullState = Sodium.cryptoSecretstreamXchacha20poly1305InitPull(
2022-06-11 08:23:52 +00:00
args["header"],
args["key"],
);
var bytesRead = 0;
var tag = Sodium.cryptoSecretstreamXchacha20poly1305TagMessage;
while (tag != Sodium.cryptoSecretstreamXchacha20poly1305TagFinal) {
var chunkSize = decryptionChunkSize;
if (bytesRead + chunkSize >= sourceFileLength) {
chunkSize = sourceFileLength - bytesRead;
}
final buffer = inputFile.readSync(chunkSize);
bytesRead += chunkSize;
final pullResult =
Sodium.cryptoSecretstreamXchacha20poly1305Pull(pullState, buffer, null);
2021-08-09 06:26:11 +00:00
await destinationFile.writeAsBytes(pullResult.m, mode: io.FileMode.append);
tag = pullResult.tag;
}
inputFile.closeSync();
2022-06-11 08:23:52 +00:00
logger.info(
"ChaCha20 Decryption time: " +
(DateTime.now().millisecondsSinceEpoch - decryptionStartTime)
.toString(),
);
}
Uint8List chachaDecryptData(Map<String, dynamic> args) {
2021-07-22 06:51:59 +00:00
final pullState = Sodium.cryptoSecretstreamXchacha20poly1305InitPull(
2022-06-11 08:23:52 +00:00
args["header"],
args["key"],
);
2021-07-22 06:51:59 +00:00
final pullResult = Sodium.cryptoSecretstreamXchacha20poly1305Pull(
2022-06-11 08:23:52 +00:00
pullState,
args["source"],
null,
);
return pullResult.m;
}
class CryptoUtil {
2021-07-22 06:51:59 +00:00
static final Computer _computer = Computer();
static init() {
_computer.turnOn(workersCount: 4);
2021-04-01 19:03:55 +00:00
Sodium.init();
}
static EncryptionResult encryptSync(Uint8List source, Uint8List key) {
final nonce = Sodium.randombytesBuf(Sodium.cryptoSecretboxNoncebytes);
2021-07-22 06:51:59 +00:00
final args = <String, dynamic>{};
args["source"] = source;
args["nonce"] = nonce;
args["key"] = key;
final encryptedData = cryptoSecretboxEasy(args);
2020-10-06 23:17:53 +00:00
return EncryptionResult(
2022-06-11 08:23:52 +00:00
key: key,
nonce: nonce,
encryptedData: encryptedData,
);
}
2020-10-06 22:58:41 +00:00
static Future<Uint8List> decrypt(
2020-10-10 22:40:18 +00:00
Uint8List cipher,
Uint8List key,
Uint8List nonce,
) async {
2021-07-22 06:51:59 +00:00
final args = <String, dynamic>{};
2020-10-06 22:58:41 +00:00
args["cipher"] = cipher;
args["nonce"] = nonce;
args["key"] = key;
return _computer.compute(cryptoSecretboxOpenEasy, param: args);
2020-10-06 22:58:41 +00:00
}
static Uint8List decryptSync(
Uint8List cipher,
Uint8List key,
Uint8List nonce,
) {
2021-07-22 06:51:59 +00:00
final args = <String, dynamic>{};
args["cipher"] = cipher;
args["nonce"] = nonce;
args["key"] = key;
return cryptoSecretboxOpenEasy(args);
}
static Future<EncryptionResult> encryptChaCha(
2022-06-11 08:23:52 +00:00
Uint8List source,
Uint8List key,
) async {
2021-07-22 06:51:59 +00:00
final args = <String, dynamic>{};
2021-05-04 15:32:23 +00:00
args["source"] = source;
args["key"] = key;
return _computer.compute(chachaEncryptData, param: args);
}
static Future<Uint8List> decryptChaCha(
2022-06-11 08:23:52 +00:00
Uint8List source,
Uint8List key,
Uint8List header,
) async {
2021-07-22 06:51:59 +00:00
final args = <String, dynamic>{};
args["source"] = source;
args["key"] = key;
args["header"] = header;
return _computer.compute(chachaDecryptData, param: args);
}
2020-10-06 23:17:53 +00:00
static Future<EncryptionResult> encryptFile(
String sourceFilePath,
String destinationFilePath, {
Uint8List key,
}) {
2021-07-22 06:51:59 +00:00
final args = <String, dynamic>{};
args["sourceFilePath"] = sourceFilePath;
args["destinationFilePath"] = destinationFilePath;
args["key"] = key;
return _computer.compute(chachaEncryptFile, param: args);
}
static Future<void> decryptFile(
String sourceFilePath,
String destinationFilePath,
2020-10-06 23:17:53 +00:00
Uint8List header,
Uint8List key,
) {
2021-07-22 06:51:59 +00:00
final args = <String, dynamic>{};
args["sourceFilePath"] = sourceFilePath;
args["destinationFilePath"] = destinationFilePath;
2020-10-06 23:17:53 +00:00
args["header"] = header;
args["key"] = key;
return _computer.compute(chachaDecryptFile, param: args);
}
static Uint8List generateKey() {
2020-10-07 00:24:13 +00:00
return Sodium.cryptoSecretboxKeygen();
}
static Uint8List getSaltToDeriveKey() {
return Sodium.randombytesBuf(Sodium.cryptoPwhashSaltbytes);
}
static Future<KeyPair> generateKeyPair() async {
return Sodium.cryptoBoxKeypair();
}
static Uint8List openSealSync(
2022-06-11 08:23:52 +00:00
Uint8List input,
Uint8List publicKey,
Uint8List secretKey,
) {
return Sodium.cryptoBoxSealOpen(input, publicKey, secretKey);
}
static Uint8List sealSync(Uint8List input, Uint8List publicKey) {
return Sodium.cryptoBoxSeal(input, publicKey);
}
static Future<DerivedKeyResult> deriveSensitiveKey(
2022-06-11 08:23:52 +00:00
Uint8List password,
Uint8List salt,
) async {
final logger = Logger("pwhash");
int memLimit = Sodium.cryptoPwhashMemlimitSensitive;
int opsLimit = Sodium.cryptoPwhashOpslimitSensitive;
Uint8List key;
while (memLimit > Sodium.cryptoPwhashMemlimitMin &&
opsLimit < Sodium.cryptoPwhashOpslimitMax) {
try {
key = await deriveKey(password, salt, memLimit, opsLimit);
return DerivedKeyResult(key, memLimit, opsLimit);
} catch (e, s) {
logger.severe(e, s);
}
memLimit = (memLimit / 2).round();
opsLimit = opsLimit * 2;
}
throw UnsupportedError("Cannot perform this operation on this device");
}
static Future<Uint8List> deriveKey(
Uint8List password,
Uint8List salt,
int memLimit,
int opsLimit,
) {
2022-06-11 08:23:52 +00:00
return _computer.compute(
cryptoPwHash,
param: {
"password": password,
"salt": salt,
"memLimit": memLimit,
"opsLimit": opsLimit,
},
);
}
2021-08-18 12:07:50 +00:00
static Future<Uint8List> getHash(io.File source) {
2022-06-11 08:23:52 +00:00
return _computer.compute(
cryptoGenericHash,
param: {
"sourceFilePath": source.path,
},
);
2021-08-18 12:07:50 +00:00
}
}