blob: 888e782eb5c667916132430dcf642747c7e8da7f [file] [view]
# AES-CBC with Pointy Castle
This article describes how to perform AES-CBC encryption and
decryption using the Pointy Castle package, which is an implementation
of cryptographic algorithms for use with the Dart programming
language.
## Overview
The Advanced Encryption Standard (AES) is a symmetric encryption
algorithm. As a symmetric algorithm, the same secret key is used to
encrypt and decrypt. It is also a block cipher algorithm, which means
the algorithm processes the data in fixed-size blocks.
Cipher Block Chaining (CBC) is a mode of operation where each block is
combined with the previous block before it is encrypted. Since the
first block doesn't have a previous block, it is combined with an
Initialization Vector (IV).
There are three algorithms in the AES family: AES-128, AES-192 and
AES-256, corresponding to the length of the keys in bits. For all the
algorithms, the block size is always 128-bits (32 bytes).
To encrypt using AES-CBC:
1. Instantiate the CBC block cipher class with the AES implementation class.
2. Initialize it with the key and Initialization Vector (IV) for encryption.
3. Process each block of the padded plaintext being encrypted.
To decrypt using AES-CBC:
1. Instantiate the CBC block cipher class with the AES implementation class.
2. Initialize it with the key and Initialization Vector (IV) for decryption.
3. Process each block of the ciphertext being decrypted.
These functions encrypts and decrypts using AES-CBC:
```dart
import 'dart:convert';
import 'dart:math';
import 'dart:typed_data';
import "package:pointycastle/export.dart";
Uint8List aesCbcEncrypt(Uint8List key, Uint8List iv, Uint8List paddedPlaintext) {
// Create a CBC block cipher with AES, and initialize with key and IV
final cbc = CBCBlockCipher(AESFastEngine())
..init(true, ParametersWithIV(KeyParameter(key), iv)); // true=encrypt
// Encrypt the plaintext block-by-block
final cipherText = Uint8List(paddedPlaintext.length); // allocate space
var offset = 0;
while (offset < paddedPlaintext.length) {
offset += cbc.processBlock(paddedPlaintext, offset, cipherText, offset);
}
assert(offset == paddedPlaintext.length);
return cipherText;
}
Uint8List aesCbcDecrypt(Uint8List key, Uint8List iv, Uint8List cipherText) {
// Create a CBC block cipher with AES, and initialize with key and IV
final cbc = CBCBlockCipher(AESFastEngine())
..init(false, ParametersWithIV(KeyParameter(key), iv)); // false=decrypt
// Decrypt the cipherText block-by-block
final paddedPlainText = Uint8List(cipherText.length); // allocate space
var offset = 0;
while (offset < cipherText.length) {
offset += cbc.processBlock(cipherText, offset, paddedPlainText, offset);
}
assert(offset == cipherText.length);
return paddedPlainText;
}
```
The _key_ must be exactly 128-bits, 192-bits or 256-bits (i.e. 16, 24
or 32 bytes). This is what determines whether AES-128, AES-192 or
AES-256 is being performed.
The _iv_ must be exactly 128-bites (16 bytes) long, which is the AES
block size.
The _paddedPlainText_ must be a multiple of the block size
(128-bits). If the data being encrypted is not the correct length, it
must be padded before it can be processed by AES.
## Details
### Implementation
#### Using the registry
If using the registry, invoke the `BlockCipher` factory with the name
of the encryption algorithm and block cipher mode: "AES/CBC".
```dart
final aesCbc = BlockCipher('AES/CBC');
```
#### Without the registry
If the registry is not used, invoke the block cipher's constructor,
passing in the AES implementation as a parameter.
```dart
final aesCbc = CBCBlockCipher(AESFastEngine());
```
### Initialize with key and IV
The first parameter determines if the object is used for encryption or
decryption.
Initialize for encryption:
```dart
aesCbc.init(true, ParametersWithIV(KeyParameter(key), iv)); // true=encrypt
```
Initialize for decryption:
```dart
aesCbc.init(false, ParametersWithIV(KeyParameter(key), iv)); // false=decrypt
```
### Processing each block
Invoke the `processBlock` method for each block, in order from the
first block to the last.
The method takes four parameters:
- Source `Uint8List` where the block comes from;
- Offset into that source where the block starts;
- Destination `Uint8List` to write the calculated block to;
- Offset into that destination where the output block will start.
Since each block is processed into another block, the destination
is the exact same size as the source.
For example,
```dart
final destination = Uint8List(source.length); // allocate space
var offset = 0;
while (offset < paddedPlaintext.length) {
offset += cbc.processBlock(source, offset, destination, offset);
}
assert(offset == source.length);
```
The process is the same for encryption and decryption. With
encryption, the source is the padded plaintext. With decryption, the
source is the ciphertext (which doesn't need padding, since it is
guaranteed to be a multiple of the block size).
## External dependencies
There is no single standard for how the IV, ciphertext, and the other
necessary information (e.g. the key length) is stored or
transmitted. Different programs and standards do it differently. To
be able to interoperate, the encrypting and decrypting programs must
agree to use the same method.
There is also no single standard for how the keys are obtained, the IV
is generated, or how the data is padded. Again, for interoperability,
the encrypting and decrypting programs must agree to use the same
method.
- The key is normally derived from a text passphrase, using a secure
key derivation algorithm. While the key must be kept secret, the
algorithm (with any parameters) used by the encrypting software must
provided to or known by the decrypting software. One of those
parameters is the size of the key, since that determines which AES
algorithm is used.
- The Initialization Vector (IV) is normally randomly generated. It
must be stored or transmitted to the decrypting software, since the
same IV must be used in decryption.
- The padding must be identified by the decrypting software, so it can
remove it from the decrypted blocks.
It is important to securely derive the key and generate the IV. While
AES itself is secure, a system is only as secure as its weakest link.