crypto_util.dart 8.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256
  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 encryptionBlockSize = 4 * 1024 * 1024;
  18. static int decryptionBlockSize =
  19. encryptionBlockSize + 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 encryptionTag = Sodium.cryptoSecretstreamXchacha20poly1305TagMessage;
  61. while (
  62. encryptionTag != Sodium.cryptoSecretstreamXchacha20poly1305TagFinal) {
  63. bool isLastBlock = false;
  64. var blockLength = encryptionBlockSize;
  65. if (bytesRead + blockLength >= sourceFileLength) {
  66. blockLength = sourceFileLength - bytesRead;
  67. isLastBlock = true;
  68. }
  69. final blockData = await inputFile.read(blockLength);
  70. bytesRead += blockLength;
  71. if (isLastBlock) {
  72. encryptionTag = Sodium.cryptoSecretstreamXchacha20poly1305TagFinal;
  73. }
  74. final encryptedData = Sodium.cryptoSecretstreamXchacha20poly1305Push(
  75. initPushResult.state, blockData, null, encryptionTag);
  76. outputFile.writeFromSync(encryptedData);
  77. }
  78. await inputFile.close();
  79. await outputFile.close();
  80. _logger.info("ChaCha20 Encryption time: " +
  81. (DateTime.now().millisecondsSinceEpoch - encryptionStartTime)
  82. .toString());
  83. return ChaChaAttributes(EncryptionAttribute(bytes: key),
  84. EncryptionAttribute(bytes: initPushResult.header));
  85. }
  86. static Future<void> chachaDecrypt(
  87. io.File sourceFile,
  88. io.File destinationFile,
  89. ChaChaAttributes attributes,
  90. ) async {
  91. var decryptionStartTime = DateTime.now().millisecondsSinceEpoch;
  92. final sourceFileLength = sourceFile.lengthSync();
  93. _logger.info("Decrypting file of size " + sourceFileLength.toString());
  94. final inputFile = await (sourceFile.open(mode: io.FileMode.read));
  95. final outputFile =
  96. await (destinationFile.open(mode: io.FileMode.writeOnlyAppend));
  97. final pullState = Sodium.cryptoSecretstreamXchacha20poly1305InitPull(
  98. attributes.header.bytes, attributes.key.bytes);
  99. var bytesRead = 0;
  100. var tag = Sodium.cryptoSecretstreamXchacha20poly1305TagMessage;
  101. while (tag != Sodium.cryptoSecretstreamXchacha20poly1305TagFinal) {
  102. var blockLength = decryptionBlockSize;
  103. if (bytesRead + blockLength >= sourceFileLength) {
  104. blockLength = sourceFileLength - bytesRead;
  105. }
  106. final blockData = await inputFile.read(blockLength);
  107. bytesRead += blockLength;
  108. final pullResult = Sodium.cryptoSecretstreamXchacha20poly1305Pull(
  109. pullState, blockData, null);
  110. outputFile.writeFromSync(pullResult.m);
  111. tag = pullResult.tag;
  112. }
  113. await inputFile.close();
  114. await outputFile.close();
  115. _logger.info("ChaCha20 Decryption time: " +
  116. (DateTime.now().millisecondsSinceEpoch - decryptionStartTime)
  117. .toString());
  118. }
  119. static Uint8List getSecureRandomBytes({int length = 32}) {
  120. return SecureRandom(length).bytes;
  121. }
  122. static String getSecureRandomString({int length = 32}) {
  123. return SecureRandom(length).base64;
  124. }
  125. static Uint8List scrypt(Uint8List plainText, Uint8List salt) {
  126. return steel.PassCryptRaw.scrypt()
  127. .hash(salt: salt, plain: plainText, len: 32);
  128. }
  129. static Uint8List aesEncrypt(
  130. Uint8List plainText, Uint8List key, Uint8List iv) {
  131. final encrypter = AES(Key(key), mode: AESMode.cbc);
  132. return encrypter
  133. .encrypt(
  134. plainText,
  135. iv: IV(iv),
  136. )
  137. .bytes;
  138. }
  139. static Uint8List aesDecrypt(
  140. Uint8List cipherText, Uint8List key, Uint8List iv) {
  141. final encrypter = AES(Key(key), mode: AESMode.cbc);
  142. return encrypter.decrypt(
  143. Encrypted(cipherText),
  144. iv: IV(iv),
  145. );
  146. }
  147. static Future<String> encryptFileToFile(
  148. String sourcePath, String destinationPath, String password) async {
  149. final args = Map<String, dynamic>();
  150. args["password"] = password;
  151. args["source"] = sourcePath;
  152. args["destination"] = destinationPath;
  153. return Computer().compute(runEncryptFileToFile, param: args);
  154. }
  155. static Future<String> encryptDataToFile(
  156. Uint8List source, String destinationPath, String password) async {
  157. final args = Map<String, dynamic>();
  158. args["password"] = password;
  159. args["source"] = source;
  160. args["destination"] = destinationPath;
  161. return Computer().compute(runEncryptDataToFile, param: args);
  162. }
  163. static Future<Uint8List> encryptDataToData(
  164. Uint8List source, String password) async {
  165. final destinationPath =
  166. Configuration.instance.getTempDirectory() + Uuid().v4();
  167. return encryptDataToFile(source, destinationPath, password).then((value) {
  168. final file = io.File(destinationPath);
  169. final data = file.readAsBytesSync();
  170. file.deleteSync();
  171. return data;
  172. });
  173. }
  174. static Future<void> decryptFileToFile(
  175. String sourcePath, String destinationPath, String password) async {
  176. final args = Map<String, dynamic>();
  177. args["password"] = password;
  178. args["source"] = sourcePath;
  179. args["destination"] = destinationPath;
  180. return Computer().compute(runDecryptFileToFile, param: args);
  181. }
  182. static Future<Uint8List> decryptFileToData(
  183. String sourcePath, String password) {
  184. final args = Map<String, dynamic>();
  185. args["password"] = password;
  186. args["source"] = sourcePath;
  187. return Computer().compute(runDecryptFileToData, param: args);
  188. }
  189. static Future<Uint8List> decryptDataToData(
  190. Uint8List source, String password) {
  191. final sourcePath = Configuration.instance.getTempDirectory() + Uuid().v4();
  192. final file = io.File(sourcePath);
  193. file.writeAsBytesSync(source);
  194. return decryptFileToData(sourcePath, password).then((value) {
  195. file.deleteSync();
  196. return value;
  197. });
  198. }
  199. }
  200. Future<String> runEncryptFileToFile(Map<String, dynamic> args) {
  201. final encrypter = getEncrypter(args["password"]);
  202. return encrypter.encryptFile(args["source"], args["destination"]);
  203. }
  204. Future<String> runEncryptDataToFile(Map<String, dynamic> args) {
  205. final encrypter = getEncrypter(args["password"]);
  206. return encrypter.encryptDataToFile(args["source"], args["destination"]);
  207. }
  208. Future<String> runDecryptFileToFile(Map<String, dynamic> args) async {
  209. final encrypter = getEncrypter(args["password"]);
  210. return encrypter.decryptFile(args["source"], args["destination"]);
  211. }
  212. Future<Uint8List> runDecryptFileToData(Map<String, dynamic> args) async {
  213. final encrypter = getEncrypter(args["password"]);
  214. return encrypter.decryptDataFromFile(args["source"]);
  215. }
  216. AesCrypt getEncrypter(String password) {
  217. final encrypter = AesCrypt(password);
  218. encrypter.aesSetMode(AesMode.cbc);
  219. encrypter.setOverwriteMode(AesCryptOwMode.on);
  220. return encrypter;
  221. }