Encrypted messaging app
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

158 lines
4.3 KiB

  1. import 'dart:convert';
  2. import 'dart:typed_data';
  3. import 'package:pointycastle/export.dart';
  4. Uint8List createUint8ListFromString(String s) {
  5. var ret = Uint8List(s.length);
  6. for (var i = 0; i < s.length; i++) {
  7. ret[i] = s.codeUnitAt(i);
  8. }
  9. return ret;
  10. }
  11. // AES key size
  12. const keySize = 32; // 32 byte key for AES-256
  13. const iterationCount = 1000;
  14. class AesHelper {
  15. static const cbcMode = 'CBC';
  16. static const cfbMode = 'CFB';
  17. static Uint8List deriveKey(dynamic password,
  18. {String salt = '',
  19. int iterationCount = iterationCount,
  20. int derivedKeyLength = keySize}) {
  21. if (password == null || password.isEmpty) {
  22. throw ArgumentError('password must not be empty');
  23. }
  24. if (password is String) {
  25. password = createUint8ListFromString(password);
  26. }
  27. Uint8List saltBytes = createUint8ListFromString(salt);
  28. Pbkdf2Parameters params = Pbkdf2Parameters(saltBytes, iterationCount, derivedKeyLength);
  29. KeyDerivator keyDerivator = PBKDF2KeyDerivator(HMac(SHA256Digest(), 64));
  30. keyDerivator.init(params);
  31. return keyDerivator.process(password);
  32. }
  33. static Uint8List pad(Uint8List src, int blockSize) {
  34. var pad = PKCS7Padding();
  35. pad.init(null);
  36. int padLength = blockSize - (src.length % blockSize);
  37. var out = Uint8List(src.length + padLength)..setAll(0, src);
  38. pad.addPadding(out, src.length);
  39. return out;
  40. }
  41. static Uint8List unpad(Uint8List src) {
  42. var pad = PKCS7Padding();
  43. pad.init(null);
  44. int padLength = pad.padCount(src);
  45. int len = src.length - padLength;
  46. return Uint8List(len)..setRange(0, len, src);
  47. }
  48. static String aesEncrypt(dynamic password, Uint8List plaintext,
  49. {String mode = cbcMode}) {
  50. Uint8List derivedKey;
  51. if (password is String) {
  52. derivedKey = deriveKey(password);
  53. } else {
  54. derivedKey = password;
  55. }
  56. KeyParameter keyParam = KeyParameter(derivedKey);
  57. BlockCipher aes = AESEngine();
  58. var rnd = FortunaRandom();
  59. rnd.seed(keyParam);
  60. Uint8List iv = rnd.nextBytes(aes.blockSize);
  61. BlockCipher cipher;
  62. ParametersWithIV params = ParametersWithIV(keyParam, iv);
  63. switch (mode) {
  64. case cbcMode:
  65. cipher = CBCBlockCipher(aes);
  66. break;
  67. case cfbMode:
  68. cipher = CFBBlockCipher(aes, aes.blockSize);
  69. break;
  70. default:
  71. throw ArgumentError('incorrect value of the "mode" parameter');
  72. }
  73. cipher.init(true, params);
  74. Uint8List paddedText = pad(plaintext, aes.blockSize);
  75. Uint8List cipherBytes = _processBlocks(cipher, paddedText);
  76. Uint8List cipherIvBytes = Uint8List(cipherBytes.length + iv.length)
  77. ..setAll(0, iv)
  78. ..setAll(iv.length, cipherBytes);
  79. return base64.encode(cipherIvBytes);
  80. }
  81. static Uint8List aesDecryptBytes(dynamic password, Uint8List ciphertext,
  82. {String mode = cbcMode}) {
  83. Uint8List derivedKey;
  84. if (password is String) {
  85. derivedKey = deriveKey(password);
  86. } else {
  87. derivedKey = password;
  88. }
  89. KeyParameter keyParam = KeyParameter(derivedKey);
  90. BlockCipher aes = AESEngine();
  91. Uint8List iv = Uint8List(aes.blockSize)
  92. ..setRange(0, aes.blockSize, ciphertext);
  93. BlockCipher cipher;
  94. ParametersWithIV params = ParametersWithIV(keyParam, iv);
  95. switch (mode) {
  96. case cbcMode:
  97. cipher = CBCBlockCipher(aes);
  98. break;
  99. case cfbMode:
  100. cipher = CFBBlockCipher(aes, aes.blockSize);
  101. break;
  102. default:
  103. throw ArgumentError('incorrect value of the "mode" parameter');
  104. }
  105. cipher.init(false, params);
  106. int cipherLen = ciphertext.length - aes.blockSize;
  107. Uint8List cipherBytes = Uint8List(cipherLen)
  108. ..setRange(0, cipherLen, ciphertext, aes.blockSize);
  109. Uint8List paddedText = _processBlocks(cipher, cipherBytes);
  110. Uint8List textBytes = unpad(paddedText);
  111. return textBytes;
  112. }
  113. static String aesDecrypt(dynamic password, Uint8List ciphertext,
  114. {String mode = cbcMode}) {
  115. return String.fromCharCodes(aesDecryptBytes(password, ciphertext, mode: mode));
  116. }
  117. static Uint8List _processBlocks(BlockCipher cipher, Uint8List inp) {
  118. var out = Uint8List(inp.lengthInBytes);
  119. for (var offset = 0; offset < inp.lengthInBytes;) {
  120. var len = cipher.processBlock(inp, offset, out, offset);
  121. offset += len;
  122. }
  123. return out;
  124. }
  125. }