From 51d10362e9eaff8fafb73844ff63cb05d1621e61 Mon Sep 17 00:00:00 2001 From: Pointcheval Hugo Date: Thu, 17 Dec 2020 22:10:27 +0100 Subject: [PATCH] Add AES cipher implementation --- lib/src/sym/AES.dart | 112 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 112 insertions(+) create mode 100644 lib/src/sym/AES.dart diff --git a/lib/src/sym/AES.dart b/lib/src/sym/AES.dart new file mode 100644 index 0000000..8a9ea4b --- /dev/null +++ b/lib/src/sym/AES.dart @@ -0,0 +1,112 @@ +// Copyright (c) 2020 +// Author: Hugo Pointcheval + +import 'dart:typed_data'; + +import '../cipher.dart'; +import '../exceptions.dart'; +import '../key.dart'; +import '../platform.dart'; + +/// Defines all available key sizes. +enum AESKeySize { bits128, bits192, bits256 } + +class AESCipher implements Cipher { + SecretKey _sk; + CipherParameters _params; + bool _isInit; + + List _supportedCipherParams = [ + CipherParameters(BlockCipherMode.CBC, Padding.PKCS5), + ]; + + @override + String get algorithm => "AES"; + + @override + SecretKey get secretKey => _sk; + + @override + CipherParameters get parameters => _params; + + @override + bool get isInitialized => _isInit; + + /// Creates an AES cipher with specified secretKey and mode/padding + AESCipher(SecretKey secretKey, CipherParameters parameters) { + if (secretKey.algorithm != "AES") { + throw CipherInitException("Invalid key type: " + secretKey.algorithm); + } else if (!_supportedCipherParams.contains(parameters)) { + throw CipherInitException("Invalid cipher parameters."); + } + _params = parameters; + _isInit = true; + } + + /// Generates a secret key of specified size, then creates an AES cipher. + AESCipher.generate(AESKeySize size, CipherParameters parameters) { + Map _supportedSizes = { + AESKeySize.bits128: 128, + AESKeySize.bits192: 192, + AESKeySize.bits256: 256 + }; + + if (!_supportedCipherParams.contains(parameters)) { + throw CipherInitException("Invalid cipher parameters."); + } else if (!_supportedSizes.containsKey(size)) { + throw CipherInitException("Invalid key size."); + } + + _sk = SecretKey.generate("AES", _supportedSizes[size]); + _params = parameters; + _isInit = true; + } + + @override + Future encrypt(Uint8List data) async { + if (!_isInit) { + throw CipherInitException('Cipher not properly initialized.'); + } else if (_sk == null || _sk.isEmpty) { + throw CipherInitException('Invalid key size.'); + } + List c = + await Platform().encrypt(data, _sk.encoded, "AES", _params); + return AESCipherText(c[0], c[1]); + } + + @override + Future decrypt(CipherText cipherText) async { + if (cipherText.algorithm != "AES") { + throw DecryptionException("This cipher text's algorithm is not AES: " + + cipherText.algorithm + + "\nYou must use an AESCipherText."); + } else if (!_isInit) { + throw CipherInitException('Cipher not properly initialized.'); + } else if (_sk == null || _sk.isEmpty) { + throw CipherInitException('Invalid key size.'); + } + List payload = [cipherText.bytes, cipherText.iv]; + Uint8List d = + await Platform().decrypt(payload, _sk.encoded, "AES", _params); + return d; + } +} + +class AESCipherText implements CipherText { + Uint8List _bytes; + Uint8List _iv; + + @override + String get algorithm => "AES"; + + @override + Uint8List get bytes => _bytes; + + @override + Uint8List get iv => _iv; + + AESCipherText(Uint8List bytes, Uint8List iv) { + _bytes = bytes; + _iv = iv; + } +}