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.

138 lines
3.9 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(String password, Uint8List plaintext,
  49. {String mode = cbcMode}) {
  50. Uint8List derivedKey = deriveKey(password);
  51. KeyParameter keyParam = KeyParameter(derivedKey);
  52. BlockCipher aes = AESEngine();
  53. var rnd = FortunaRandom();
  54. rnd.seed(keyParam);
  55. Uint8List iv = rnd.nextBytes(aes.blockSize);
  56. BlockCipher cipher;
  57. ParametersWithIV params = ParametersWithIV(keyParam, iv);
  58. switch (mode) {
  59. case cbcMode:
  60. cipher = CBCBlockCipher(aes);
  61. break;
  62. case cfbMode:
  63. cipher = CFBBlockCipher(aes, aes.blockSize);
  64. break;
  65. default:
  66. throw ArgumentError('incorrect value of the "mode" parameter');
  67. }
  68. cipher.init(true, params);
  69. Uint8List paddedText = pad(plaintext, aes.blockSize);
  70. Uint8List cipherBytes = _processBlocks(cipher, paddedText);
  71. Uint8List cipherIvBytes = Uint8List(cipherBytes.length + iv.length)
  72. ..setAll(0, iv)
  73. ..setAll(iv.length, cipherBytes);
  74. return base64.encode(cipherIvBytes);
  75. }
  76. static String aesDecrypt(String password, Uint8List ciphertext,
  77. {String mode = cbcMode}) {
  78. Uint8List derivedKey = deriveKey(password);
  79. KeyParameter keyParam = KeyParameter(derivedKey);
  80. BlockCipher aes = AESEngine();
  81. Uint8List iv = Uint8List(aes.blockSize)
  82. ..setRange(0, aes.blockSize, ciphertext);
  83. BlockCipher cipher;
  84. ParametersWithIV params = ParametersWithIV(keyParam, iv);
  85. switch (mode) {
  86. case cbcMode:
  87. cipher = CBCBlockCipher(aes);
  88. break;
  89. case cfbMode:
  90. cipher = CFBBlockCipher(aes, aes.blockSize);
  91. break;
  92. default:
  93. throw ArgumentError('incorrect value of the "mode" parameter');
  94. }
  95. cipher.init(false, params);
  96. int cipherLen = ciphertext.length - aes.blockSize;
  97. Uint8List cipherBytes = Uint8List(cipherLen)
  98. ..setRange(0, cipherLen, ciphertext, aes.blockSize);
  99. Uint8List paddedText = _processBlocks(cipher, cipherBytes);
  100. Uint8List textBytes = unpad(paddedText);
  101. return String.fromCharCodes(textBytes);
  102. }
  103. static Uint8List _processBlocks(BlockCipher cipher, Uint8List inp) {
  104. var out = Uint8List(inp.lengthInBytes);
  105. for (var offset = 0; offset < inp.lengthInBytes;) {
  106. var len = cipher.processBlock(inp, offset, out, offset);
  107. offset += len;
  108. }
  109. return out;
  110. }
  111. }