blob: 57b65d30f4cad76e871096f5684b0ec7f8d0be48 [file] [log] [blame] [edit]
// See file LICENSE for more information.
library impl.block_cipher.modes.cbc;
import 'dart:typed_data';
import 'package:pointycastle/api.dart';
import 'package:pointycastle/src/registry/registry.dart';
import 'package:pointycastle/src/impl/base_block_cipher.dart';
/// Implementation of Cipher-Block-Chaining (CBC) mode on top of a [BlockCipher].
class CBCBlockCipher extends BaseBlockCipher {
/// Intended for internal use.
static final FactoryConfig factoryConfig = DynamicFactoryConfig.suffix(
BlockCipher,
'/CBC',
(_, final Match match) => () {
var underlying = BlockCipher(match.group(1)!);
return CBCBlockCipher(underlying);
});
final BlockCipher _underlyingCipher;
late Uint8List _iv;
Uint8List? _cbcV;
Uint8List? _cbcNextV;
late bool _encrypting;
CBCBlockCipher(this._underlyingCipher) {
_iv = Uint8List(blockSize);
_cbcV = Uint8List(blockSize);
_cbcNextV = Uint8List(blockSize);
}
@override
String get algorithmName => '${_underlyingCipher.algorithmName}/CBC';
@override
int get blockSize => _underlyingCipher.blockSize;
@override
void reset() {
_cbcV!.setAll(0, _iv);
_cbcNextV!.fillRange(0, _cbcNextV!.length, 0);
_underlyingCipher.reset();
}
@override
void init(bool forEncryption, covariant ParametersWithIV params) {
if (params.iv.length != blockSize) {
throw ArgumentError(
'Initialization vector must be the same length as block size');
}
_encrypting = forEncryption;
_iv.setAll(0, params.iv);
reset();
_underlyingCipher.init(forEncryption, params.parameters);
}
@override
int processBlock(Uint8List inp, int inpOff, Uint8List out, int outOff) =>
_encrypting
? _encryptBlock(inp, inpOff, out, outOff)
: _decryptBlock(inp, inpOff, out, outOff);
int _encryptBlock(Uint8List inp, int inpOff, Uint8List out, int outOff) {
if ((inpOff + blockSize) > inp.length) {
throw ArgumentError('Input buffer too short');
}
// XOR the cbcV and the input, then encrypt the cbcV
for (var i = 0; i < blockSize; i++) {
_cbcV![i] ^= inp[inpOff + i];
}
var length = _underlyingCipher.processBlock(_cbcV!, 0, out, outOff);
// copy ciphertext to cbcV
_cbcV!.setRange(0, blockSize,
Uint8List.view(out.buffer, out.offsetInBytes + outOff, blockSize));
return length;
}
int _decryptBlock(Uint8List inp, int inpOff, Uint8List out, int outOff) {
if ((inpOff + blockSize) > inp.length) {
throw ArgumentError('Input buffer too short');
}
_cbcNextV!.setRange(0, blockSize,
Uint8List.view(inp.buffer, inp.offsetInBytes + inpOff, blockSize));
var length = _underlyingCipher.processBlock(inp, inpOff, out, outOff);
// XOR the cbcV and the output
for (var i = 0; i < blockSize; i++) {
out[outOff + i] ^= _cbcV![i];
}
// swap the back up buffer into next position
Uint8List? tmp;
tmp = _cbcV;
_cbcV = _cbcNextV;
_cbcNextV = tmp;
return length;
}
}