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

import 'dart:convert';
import 'dart:typed_data';
import 'package:pointycastle/export.dart';
Uint8List createUint8ListFromString(String s) {
var ret = Uint8List(s.length);
for (var i = 0; i < s.length; i++) {
ret[i] = s.codeUnitAt(i);
}
return ret;
}
// AES key size
const keySize = 32; // 32 byte key for AES-256
const iterationCount = 1000;
class AesHelper {
static const cbcMode = 'CBC';
static const cfbMode = 'CFB';
static Uint8List deriveKey(dynamic password,
{String salt = '',
int iterationCount = iterationCount,
int derivedKeyLength = keySize}) {
if (password == null || password.isEmpty) {
throw ArgumentError('password must not be empty');
}
if (password is String) {
password = createUint8ListFromString(password);
}
Uint8List saltBytes = createUint8ListFromString(salt);
Pbkdf2Parameters params = Pbkdf2Parameters(saltBytes, iterationCount, derivedKeyLength);
KeyDerivator keyDerivator = PBKDF2KeyDerivator(HMac(SHA256Digest(), 64));
keyDerivator.init(params);
return keyDerivator.process(password);
}
static Uint8List pad(Uint8List src, int blockSize) {
var pad = PKCS7Padding();
pad.init(null);
int padLength = blockSize - (src.length % blockSize);
var out = Uint8List(src.length + padLength)..setAll(0, src);
pad.addPadding(out, src.length);
return out;
}
static Uint8List unpad(Uint8List src) {
var pad = PKCS7Padding();
pad.init(null);
int padLength = pad.padCount(src);
int len = src.length - padLength;
return Uint8List(len)..setRange(0, len, src);
}
static String aesEncrypt(dynamic password, Uint8List plaintext,
{String mode = cbcMode}) {
Uint8List derivedKey;
if (password is String) {
derivedKey = deriveKey(password);
} else {
derivedKey = password;
}
KeyParameter keyParam = KeyParameter(derivedKey);
BlockCipher aes = AESEngine();
var rnd = FortunaRandom();
rnd.seed(keyParam);
Uint8List iv = rnd.nextBytes(aes.blockSize);
BlockCipher cipher;
ParametersWithIV params = ParametersWithIV(keyParam, iv);
switch (mode) {
case cbcMode:
cipher = CBCBlockCipher(aes);
break;
case cfbMode:
cipher = CFBBlockCipher(aes, aes.blockSize);
break;
default:
throw ArgumentError('incorrect value of the "mode" parameter');
}
cipher.init(true, params);
Uint8List paddedText = pad(plaintext, aes.blockSize);
Uint8List cipherBytes = _processBlocks(cipher, paddedText);
Uint8List cipherIvBytes = Uint8List(cipherBytes.length + iv.length)
..setAll(0, iv)
..setAll(iv.length, cipherBytes);
return base64.encode(cipherIvBytes);
}
static Uint8List aesDecryptBytes(dynamic password, Uint8List ciphertext,
{String mode = cbcMode}) {
Uint8List derivedKey;
if (password is String) {
derivedKey = deriveKey(password);
} else {
derivedKey = password;
}
KeyParameter keyParam = KeyParameter(derivedKey);
BlockCipher aes = AESEngine();
Uint8List iv = Uint8List(aes.blockSize)
..setRange(0, aes.blockSize, ciphertext);
BlockCipher cipher;
ParametersWithIV params = ParametersWithIV(keyParam, iv);
switch (mode) {
case cbcMode:
cipher = CBCBlockCipher(aes);
break;
case cfbMode:
cipher = CFBBlockCipher(aes, aes.blockSize);
break;
default:
throw ArgumentError('incorrect value of the "mode" parameter');
}
cipher.init(false, params);
int cipherLen = ciphertext.length - aes.blockSize;
Uint8List cipherBytes = Uint8List(cipherLen)
..setRange(0, cipherLen, ciphertext, aes.blockSize);
Uint8List paddedText = _processBlocks(cipher, cipherBytes);
Uint8List textBytes = unpad(paddedText);
return textBytes;
}
static String aesDecrypt(dynamic password, Uint8List ciphertext,
{String mode = cbcMode}) {
return String.fromCharCodes(aesDecryptBytes(password, ciphertext, mode: mode));
}
static Uint8List _processBlocks(BlockCipher cipher, Uint8List inp) {
var out = Uint8List(inp.lengthInBytes);
for (var offset = 0; offset < inp.lengthInBytes;) {
var len = cipher.processBlock(inp, offset, out, offset);
offset += len;
}
return out;
}
}