crypto_util.dart 6.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185
  1. import 'dart:typed_data';
  2. import 'dart:io' as io;
  3. import 'package:computer/computer.dart';
  4. import 'package:encrypt/encrypt.dart';
  5. import 'package:encrypt/encrypt.dart' as e;
  6. import 'package:flutter_sodium/flutter_sodium.dart';
  7. import 'package:logging/logging.dart';
  8. import 'package:photos/models/encrypted_data_attributes.dart';
  9. import 'package:photos/models/encrypted_file_attributes.dart';
  10. import 'package:photos/models/encryption_attribute.dart';
  11. import 'package:steel_crypt/steel_crypt.dart' as steel;
  12. final int encryptionChunkSize = 4 * 1024 * 1024;
  13. final int decryptionChunkSize =
  14. encryptionChunkSize + Sodium.cryptoSecretstreamXchacha20poly1305Abytes;
  15. Uint8List cryptoSecretboxEasy(Map<String, dynamic> args) {
  16. return Sodium.cryptoSecretboxEasy(args["source"], args["nonce"], args["key"]);
  17. }
  18. Uint8List cryptoSecretboxOpenEasy(Map<String, dynamic> args) {
  19. return Sodium.cryptoSecretboxOpenEasy(
  20. args["cipher"], args["nonce"], args["key"]);
  21. }
  22. ChaChaAttributes chachaEncrypt(Map<String, dynamic> args) {
  23. final encryptionStartTime = DateTime.now().millisecondsSinceEpoch;
  24. final logger = Logger("ChaChaEncrypt");
  25. final sourceFile = io.File(args["sourceFilePath"]);
  26. final destinationFile = io.File(args["destinationFilePath"]);
  27. final sourceFileLength = sourceFile.lengthSync();
  28. logger.info("Encrypting file of size " + sourceFileLength.toString());
  29. final inputFile = sourceFile.openSync(mode: io.FileMode.read);
  30. final key = Sodium.cryptoSecretstreamXchacha20poly1305Keygen();
  31. final initPushResult =
  32. Sodium.cryptoSecretstreamXchacha20poly1305InitPush(key);
  33. var bytesRead = 0;
  34. var tag = Sodium.cryptoSecretstreamXchacha20poly1305TagMessage;
  35. while (tag != Sodium.cryptoSecretstreamXchacha20poly1305TagFinal) {
  36. var chunkSize = encryptionChunkSize;
  37. if (bytesRead + chunkSize >= sourceFileLength) {
  38. chunkSize = sourceFileLength - bytesRead;
  39. tag = Sodium.cryptoSecretstreamXchacha20poly1305TagFinal;
  40. }
  41. final buffer = inputFile.readSync(chunkSize);
  42. bytesRead += chunkSize;
  43. final encryptedData = Sodium.cryptoSecretstreamXchacha20poly1305Push(
  44. initPushResult.state, buffer, null, tag);
  45. destinationFile.writeAsBytesSync(encryptedData, mode: io.FileMode.append);
  46. }
  47. inputFile.closeSync();
  48. logger.info("Encryption time: " +
  49. (DateTime.now().millisecondsSinceEpoch - encryptionStartTime).toString());
  50. return ChaChaAttributes(EncryptionAttribute(bytes: key),
  51. EncryptionAttribute(bytes: initPushResult.header));
  52. }
  53. void chachaDecrypt(Map<String, dynamic> args) {
  54. final logger = Logger("ChaChaDecrypt");
  55. final decryptionStartTime = DateTime.now().millisecondsSinceEpoch;
  56. final sourceFile = io.File(args["sourceFilePath"]);
  57. final destinationFile = io.File(args["destinationFilePath"]);
  58. final sourceFileLength = sourceFile.lengthSync();
  59. logger.info("Decrypting file of size " + sourceFileLength.toString());
  60. final inputFile = sourceFile.openSync(mode: io.FileMode.read);
  61. final pullState = Sodium.cryptoSecretstreamXchacha20poly1305InitPull(
  62. args["header"], args["key"]);
  63. var bytesRead = 0;
  64. var tag = Sodium.cryptoSecretstreamXchacha20poly1305TagMessage;
  65. while (tag != Sodium.cryptoSecretstreamXchacha20poly1305TagFinal) {
  66. var chunkSize = decryptionChunkSize;
  67. if (bytesRead + chunkSize >= sourceFileLength) {
  68. chunkSize = sourceFileLength - bytesRead;
  69. }
  70. final buffer = inputFile.readSync(chunkSize);
  71. bytesRead += chunkSize;
  72. final pullResult =
  73. Sodium.cryptoSecretstreamXchacha20poly1305Pull(pullState, buffer, null);
  74. destinationFile.writeAsBytesSync(pullResult.m, mode: io.FileMode.append);
  75. tag = pullResult.tag;
  76. }
  77. inputFile.closeSync();
  78. logger.info("ChaCha20 Decryption time: " +
  79. (DateTime.now().millisecondsSinceEpoch - decryptionStartTime).toString());
  80. }
  81. class CryptoUtil {
  82. static Future<EncryptedData> encrypt(Uint8List source,
  83. {Uint8List key}) async {
  84. if (key == null) {
  85. key = Sodium.cryptoSecretboxKeygen();
  86. }
  87. final nonce = Sodium.randombytesBuf(Sodium.cryptoSecretboxNoncebytes);
  88. final args = Map<String, dynamic>();
  89. args["source"] = source;
  90. args["nonce"] = nonce;
  91. args["key"] = key;
  92. final encryptedData =
  93. await Computer().compute(cryptoSecretboxEasy, param: args);
  94. return EncryptedData(
  95. EncryptionAttribute(bytes: key),
  96. EncryptionAttribute(bytes: nonce),
  97. EncryptionAttribute(bytes: encryptedData));
  98. }
  99. static Future<Uint8List> decrypt(
  100. Uint8List cipher, Uint8List key, Uint8List nonce,
  101. {bool background = false}) async {
  102. final args = Map<String, dynamic>();
  103. args["cipher"] = cipher;
  104. args["nonce"] = nonce;
  105. args["key"] = key;
  106. if (background) {
  107. return Computer().compute(cryptoSecretboxOpenEasy, param: args);
  108. } else {
  109. return cryptoSecretboxOpenEasy(args);
  110. }
  111. }
  112. static Future<ChaChaAttributes> encryptFile(
  113. String sourceFilePath,
  114. String destinationFilePath,
  115. ) {
  116. final args = Map<String, dynamic>();
  117. args["sourceFilePath"] = sourceFilePath;
  118. args["destinationFilePath"] = destinationFilePath;
  119. return Computer().compute(chachaEncrypt, param: args);
  120. }
  121. static Future<void> decryptFile(
  122. String sourceFilePath,
  123. String destinationFilePath,
  124. ChaChaAttributes attributes,
  125. ) {
  126. final args = Map<String, dynamic>();
  127. args["sourceFilePath"] = sourceFilePath;
  128. args["destinationFilePath"] = destinationFilePath;
  129. args["header"] = attributes.header.bytes;
  130. args["key"] = attributes.key.bytes;
  131. return Computer().compute(chachaDecrypt, param: args);
  132. }
  133. static Uint8List getSecureRandomBytes({int length = 32}) {
  134. return SecureRandom(length).bytes;
  135. }
  136. static String getSecureRandomString({int length = 32}) {
  137. return SecureRandom(length).base64;
  138. }
  139. static Uint8List scrypt(Uint8List plainText, Uint8List salt) {
  140. return steel.PassCryptRaw.scrypt()
  141. .hash(salt: salt, plain: plainText, len: 32);
  142. }
  143. static Uint8List aesEncrypt(
  144. Uint8List plainText, Uint8List key, Uint8List iv) {
  145. final encrypter = AES(e.Key(key), mode: AESMode.cbc);
  146. return encrypter
  147. .encrypt(
  148. plainText,
  149. iv: IV(iv),
  150. )
  151. .bytes;
  152. }
  153. static Uint8List aesDecrypt(
  154. Uint8List cipherText, Uint8List key, Uint8List iv) {
  155. final encrypter = AES(e.Key(key), mode: AESMode.cbc);
  156. return encrypter.decrypt(
  157. Encrypted(cipherText),
  158. iv: IV(iv),
  159. );
  160. }
  161. }