113 lines
3 KiB
Dart
113 lines
3 KiB
Dart
// 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;
|
|
}
|
|
}
|