crypto_util.dart 9.4 KB

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