crypto_util.dart 6.8 KB

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