crypto_util.dart 7.8 KB

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