crypto_util.dart 7.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226
  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 = 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 EncryptionResult encryptSync(Uint8List source, Uint8List key) {
  85. final nonce = Sodium.randombytesBuf(Sodium.cryptoSecretboxNoncebytes);
  86. final args = Map<String, dynamic>();
  87. args["source"] = source;
  88. args["nonce"] = nonce;
  89. args["key"] = key;
  90. final encryptedData = cryptoSecretboxEasy(args);
  91. return EncryptionResult(
  92. key: key, nonce: nonce, encryptedData: encryptedData);
  93. }
  94. static Future<Uint8List> decrypt(
  95. Uint8List cipher,
  96. Uint8List key,
  97. Uint8List nonce,
  98. ) async {
  99. final args = Map<String, dynamic>();
  100. args["cipher"] = cipher;
  101. args["nonce"] = nonce;
  102. args["key"] = key;
  103. return Computer().compute(cryptoSecretboxOpenEasy, param: args);
  104. }
  105. static Uint8List decryptSync(
  106. Uint8List cipher,
  107. Uint8List key,
  108. Uint8List nonce,
  109. ) {
  110. final args = Map<String, dynamic>();
  111. args["cipher"] = cipher;
  112. args["nonce"] = nonce;
  113. args["key"] = key;
  114. return cryptoSecretboxOpenEasy(args);
  115. }
  116. static EncryptionResult encryptChaCha(Uint8List source, Uint8List key) {
  117. final initPushResult =
  118. Sodium.cryptoSecretstreamXchacha20poly1305InitPush(key);
  119. final encryptedData = Sodium.cryptoSecretstreamXchacha20poly1305Push(
  120. initPushResult.state,
  121. source,
  122. null,
  123. Sodium.cryptoSecretstreamXchacha20poly1305TagFinal);
  124. return EncryptionResult(
  125. encryptedData: encryptedData, header: initPushResult.header);
  126. }
  127. static Uint8List decryptChaCha(
  128. Uint8List source, Uint8List key, Uint8List header) {
  129. final pullState =
  130. Sodium.cryptoSecretstreamXchacha20poly1305InitPull(header, key);
  131. final pullResult =
  132. Sodium.cryptoSecretstreamXchacha20poly1305Pull(pullState, source, null);
  133. return pullResult.m;
  134. }
  135. static Future<EncryptionResult> 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. Uint8List header,
  148. Uint8List key,
  149. ) {
  150. final args = Map<String, dynamic>();
  151. args["sourceFilePath"] = sourceFilePath;
  152. args["destinationFilePath"] = destinationFilePath;
  153. args["header"] = header;
  154. args["key"] = key;
  155. return Computer().compute(chachaDecrypt, param: args);
  156. }
  157. static Uint8List generateKey() {
  158. return Sodium.cryptoSecretboxKeygen();
  159. }
  160. static Uint8List getSaltToDeriveKey() {
  161. return Sodium.randombytesBuf(Sodium.cryptoPwhashSaltbytes);
  162. }
  163. static Uint8List deriveKey(Uint8List passphrase, Uint8List salt) {
  164. return Sodium.cryptoPwhash(
  165. Sodium.cryptoSecretboxKeybytes,
  166. passphrase,
  167. salt,
  168. Sodium.cryptoPwhashOpslimitInteractive,
  169. Sodium.cryptoPwhashMemlimitInteractive,
  170. Sodium.cryptoPwhashAlgDefault);
  171. }
  172. static Future<String> hash(Uint8List input) async {
  173. Sodium.init();
  174. final args = Map<String, dynamic>();
  175. args["input"] = input;
  176. args["opsLimit"] = Sodium.cryptoPwhashOpslimitSensitive;
  177. args["memLimit"] = Sodium.cryptoPwhashMemlimitModerate;
  178. return utf8.decode(await Computer().compute(cryptoPwhashStr, param: args));
  179. }
  180. static Future<bool> verifyHash(Uint8List input, String hash) async {
  181. final args = Map<String, dynamic>();
  182. args["input"] = input;
  183. args["hash"] = utf8.encode(hash);
  184. return await Computer().compute(cryptoPwhashStrVerify, param: args);
  185. }
  186. static Future<KeyPair> generateKeyPair() async {
  187. return Sodium.cryptoBoxKeypair();
  188. }
  189. static Uint8List openSealSync(
  190. Uint8List input, Uint8List publicKey, Uint8List secretKey) {
  191. return Sodium.cryptoBoxSealOpen(input, publicKey, secretKey);
  192. }
  193. static Uint8List sealSync(Uint8List input, Uint8List publicKey) {
  194. return Sodium.cryptoBoxSeal(input, publicKey);
  195. }
  196. }