crypto_util.dart 8.8 KB

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