crypto_util.dart 7.6 KB

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