crypto_util.dart 8.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251
  1. import 'dart:typed_data';
  2. import 'dart:io' as io;
  3. import 'package:aes_crypt/aes_crypt.dart';
  4. import 'package:computer/computer.dart';
  5. import 'package:encrypt/encrypt.dart';
  6. import 'package:flutter_sodium/flutter_sodium.dart';
  7. import 'package:logging/logging.dart';
  8. import 'package:photos/core/configuration.dart';
  9. import 'package:photos/models/decryption_params.dart';
  10. import 'package:photos/models/encrypted_data_attributes.dart';
  11. import 'package:photos/models/encrypted_file_attributes.dart';
  12. import 'package:photos/models/encryption_attribute.dart';
  13. import 'package:steel_crypt/steel_crypt.dart' as steel;
  14. import 'package:uuid/uuid.dart';
  15. class CryptoUtil {
  16. static Logger _logger = Logger("CryptoUtil");
  17. static int encryptionChunkSize = 4 * 1024 * 1024;
  18. static int decryptionChunkSize =
  19. encryptionChunkSize + Sodium.cryptoSecretstreamXchacha20poly1305Abytes;
  20. static Future<EncryptedData> encrypt(Uint8List source,
  21. {Uint8List key}) async {
  22. if (key == null) {
  23. key = Sodium.cryptoSecretboxKeygen();
  24. }
  25. final nonce = Sodium.randombytesBuf(Sodium.cryptoSecretboxNoncebytes);
  26. final encryptedData = Sodium.cryptoSecretboxEasy(source, nonce, key);
  27. return EncryptedData(
  28. EncryptionAttribute(bytes: key),
  29. EncryptionAttribute(bytes: nonce),
  30. EncryptionAttribute(bytes: encryptedData));
  31. }
  32. static Future<Uint8List> decrypt(
  33. String base64Cipher, String base64Key, String base64Nonce) async {
  34. return Sodium.cryptoSecretboxOpenEasy(Sodium.base642bin(base64Cipher),
  35. Sodium.base642bin(base64Nonce), Sodium.base642bin(base64Key));
  36. }
  37. static Future<Uint8List> decryptWithDecryptionParams(
  38. Uint8List source, DecryptionParams params, String base64Kek) async {
  39. final key = Sodium.cryptoSecretboxOpenEasy(
  40. Sodium.base642bin(params.encryptedKey),
  41. Sodium.base642bin(params.keyDecryptionNonce),
  42. Sodium.base642bin(base64Kek));
  43. return Sodium.cryptoSecretboxOpenEasy(
  44. source, Sodium.base642bin(params.nonce), key);
  45. }
  46. static Future<ChaChaAttributes> chachaEncrypt(
  47. io.File sourceFile,
  48. io.File destinationFile,
  49. ) async {
  50. var encryptionStartTime = DateTime.now().millisecondsSinceEpoch;
  51. final sourceFileLength = sourceFile.lengthSync();
  52. _logger.info("Encrypting file of size " + sourceFileLength.toString());
  53. final inputFile = await (sourceFile.open(mode: io.FileMode.read));
  54. final outputFile =
  55. await (destinationFile.open(mode: io.FileMode.writeOnlyAppend));
  56. final key = Sodium.cryptoSecretstreamXchacha20poly1305Keygen();
  57. final initPushResult =
  58. Sodium.cryptoSecretstreamXchacha20poly1305InitPush(key);
  59. var bytesRead = 0;
  60. var tag = Sodium.cryptoSecretstreamXchacha20poly1305TagMessage;
  61. while (tag != Sodium.cryptoSecretstreamXchacha20poly1305TagFinal) {
  62. var chunkSize = encryptionChunkSize;
  63. if (bytesRead + chunkSize >= sourceFileLength) {
  64. chunkSize = sourceFileLength - bytesRead;
  65. tag = Sodium.cryptoSecretstreamXchacha20poly1305TagFinal;
  66. }
  67. final buffer = await inputFile.read(chunkSize);
  68. bytesRead += chunkSize;
  69. final encryptedData = Sodium.cryptoSecretstreamXchacha20poly1305Push(
  70. initPushResult.state, buffer, null, tag);
  71. outputFile.writeFromSync(encryptedData);
  72. }
  73. await inputFile.close();
  74. await outputFile.close();
  75. _logger.info("ChaCha20 Encryption time: " +
  76. (DateTime.now().millisecondsSinceEpoch - encryptionStartTime)
  77. .toString());
  78. return ChaChaAttributes(EncryptionAttribute(bytes: key),
  79. EncryptionAttribute(bytes: initPushResult.header));
  80. }
  81. static Future<void> chachaDecrypt(
  82. io.File sourceFile,
  83. io.File destinationFile,
  84. ChaChaAttributes attributes,
  85. ) async {
  86. var decryptionStartTime = DateTime.now().millisecondsSinceEpoch;
  87. final sourceFileLength = sourceFile.lengthSync();
  88. _logger.info("Decrypting file of size " + sourceFileLength.toString());
  89. final inputFile = await (sourceFile.open(mode: io.FileMode.read));
  90. final outputFile =
  91. await (destinationFile.open(mode: io.FileMode.writeOnlyAppend));
  92. final pullState = Sodium.cryptoSecretstreamXchacha20poly1305InitPull(
  93. attributes.header.bytes, attributes.key.bytes);
  94. var bytesRead = 0;
  95. var tag = Sodium.cryptoSecretstreamXchacha20poly1305TagMessage;
  96. while (tag != Sodium.cryptoSecretstreamXchacha20poly1305TagFinal) {
  97. var chunkSize = decryptionChunkSize;
  98. if (bytesRead + chunkSize >= sourceFileLength) {
  99. chunkSize = sourceFileLength - bytesRead;
  100. }
  101. final buffer = await inputFile.read(chunkSize);
  102. bytesRead += chunkSize;
  103. final pullResult = Sodium.cryptoSecretstreamXchacha20poly1305Pull(
  104. pullState, buffer, null);
  105. outputFile.writeFromSync(pullResult.m);
  106. tag = pullResult.tag;
  107. }
  108. await inputFile.close();
  109. await outputFile.close();
  110. _logger.info("ChaCha20 Decryption time: " +
  111. (DateTime.now().millisecondsSinceEpoch - decryptionStartTime)
  112. .toString());
  113. }
  114. static Uint8List getSecureRandomBytes({int length = 32}) {
  115. return SecureRandom(length).bytes;
  116. }
  117. static String getSecureRandomString({int length = 32}) {
  118. return SecureRandom(length).base64;
  119. }
  120. static Uint8List scrypt(Uint8List plainText, Uint8List salt) {
  121. return steel.PassCryptRaw.scrypt()
  122. .hash(salt: salt, plain: plainText, len: 32);
  123. }
  124. static Uint8List aesEncrypt(
  125. Uint8List plainText, Uint8List key, Uint8List iv) {
  126. final encrypter = AES(Key(key), mode: AESMode.cbc);
  127. return encrypter
  128. .encrypt(
  129. plainText,
  130. iv: IV(iv),
  131. )
  132. .bytes;
  133. }
  134. static Uint8List aesDecrypt(
  135. Uint8List cipherText, Uint8List key, Uint8List iv) {
  136. final encrypter = AES(Key(key), mode: AESMode.cbc);
  137. return encrypter.decrypt(
  138. Encrypted(cipherText),
  139. iv: IV(iv),
  140. );
  141. }
  142. static Future<String> encryptFileToFile(
  143. String sourcePath, String destinationPath, String password) async {
  144. final args = Map<String, dynamic>();
  145. args["password"] = password;
  146. args["source"] = sourcePath;
  147. args["destination"] = destinationPath;
  148. return Computer().compute(runEncryptFileToFile, param: args);
  149. }
  150. static Future<String> encryptDataToFile(
  151. Uint8List source, String destinationPath, String password) async {
  152. final args = Map<String, dynamic>();
  153. args["password"] = password;
  154. args["source"] = source;
  155. args["destination"] = destinationPath;
  156. return Computer().compute(runEncryptDataToFile, param: args);
  157. }
  158. static Future<Uint8List> encryptDataToData(
  159. Uint8List source, String password) async {
  160. final destinationPath =
  161. Configuration.instance.getTempDirectory() + Uuid().v4();
  162. return encryptDataToFile(source, destinationPath, password).then((value) {
  163. final file = io.File(destinationPath);
  164. final data = file.readAsBytesSync();
  165. file.deleteSync();
  166. return data;
  167. });
  168. }
  169. static Future<void> decryptFileToFile(
  170. String sourcePath, String destinationPath, String password) async {
  171. final args = Map<String, dynamic>();
  172. args["password"] = password;
  173. args["source"] = sourcePath;
  174. args["destination"] = destinationPath;
  175. return Computer().compute(runDecryptFileToFile, param: args);
  176. }
  177. static Future<Uint8List> decryptFileToData(
  178. String sourcePath, String password) {
  179. final args = Map<String, dynamic>();
  180. args["password"] = password;
  181. args["source"] = sourcePath;
  182. return Computer().compute(runDecryptFileToData, param: args);
  183. }
  184. static Future<Uint8List> decryptDataToData(
  185. Uint8List source, String password) {
  186. final sourcePath = Configuration.instance.getTempDirectory() + Uuid().v4();
  187. final file = io.File(sourcePath);
  188. file.writeAsBytesSync(source);
  189. return decryptFileToData(sourcePath, password).then((value) {
  190. file.deleteSync();
  191. return value;
  192. });
  193. }
  194. }
  195. Future<String> runEncryptFileToFile(Map<String, dynamic> args) {
  196. final encrypter = getEncrypter(args["password"]);
  197. return encrypter.encryptFile(args["source"], args["destination"]);
  198. }
  199. Future<String> runEncryptDataToFile(Map<String, dynamic> args) {
  200. final encrypter = getEncrypter(args["password"]);
  201. return encrypter.encryptDataToFile(args["source"], args["destination"]);
  202. }
  203. Future<String> runDecryptFileToFile(Map<String, dynamic> args) async {
  204. final encrypter = getEncrypter(args["password"]);
  205. return encrypter.decryptFile(args["source"], args["destination"]);
  206. }
  207. Future<Uint8List> runDecryptFileToData(Map<String, dynamic> args) async {
  208. final encrypter = getEncrypter(args["password"]);
  209. return encrypter.decryptDataFromFile(args["source"]);
  210. }
  211. AesCrypt getEncrypter(String password) {
  212. final encrypter = AesCrypt(password);
  213. encrypter.aesSetMode(AesMode.cbc);
  214. encrypter.setOverwriteMode(AesCryptOwMode.on);
  215. return encrypter;
  216. }