175 lines
6.1 KiB
Dart
175 lines
6.1 KiB
Dart
// See file LICENSE for more information.
|
|
|
|
library impl.block_cipher.modes.cfb;
|
|
|
|
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 Feedback Mode (CFB) on top of a [BlockCipher].
|
|
class CFBBlockCipher extends BaseBlockCipher {
|
|
/// Intended for internal use.
|
|
static final FactoryConfig factoryConfig = DynamicFactoryConfig.regex(
|
|
BlockCipher,
|
|
r'^(.+)/CFB-([0-9]+)$',
|
|
(_, final Match match) => () {
|
|
var underlying = BlockCipher(match.group(1)!);
|
|
var blockSizeInBits = int.parse(match.group(2)!);
|
|
if ((blockSizeInBits % 8) != 0) {
|
|
throw RegistryFactoryException.invalid(
|
|
'Bad CFB block size: $blockSizeInBits (must be a multiple of 8)');
|
|
}
|
|
return CFBBlockCipher(underlying, blockSizeInBits ~/ 8);
|
|
});
|
|
|
|
@override
|
|
final int blockSize;
|
|
|
|
final BlockCipher _underlyingCipher;
|
|
|
|
late Uint8List _iv;
|
|
Uint8List? _cfbV;
|
|
Uint8List? _cfbOutV;
|
|
late bool _encrypting;
|
|
|
|
CFBBlockCipher(this._underlyingCipher, this.blockSize) {
|
|
_iv = Uint8List(_underlyingCipher.blockSize);
|
|
_cfbV = Uint8List(_underlyingCipher.blockSize);
|
|
_cfbOutV = Uint8List(_underlyingCipher.blockSize);
|
|
}
|
|
|
|
@override
|
|
String get algorithmName =>
|
|
'${_underlyingCipher.algorithmName}/CFB-${blockSize * 8}';
|
|
|
|
@override
|
|
void reset() {
|
|
_cfbV!.setRange(0, _iv.length, _iv);
|
|
_underlyingCipher.reset();
|
|
}
|
|
|
|
/// Initialise the cipher and, possibly, the initialisation vector (IV).
|
|
/// If an IV isn't passed as part of the parameter, the IV will be all zeros.
|
|
/// An IV which is too short is handled in FIPS compliant fashion.
|
|
///
|
|
/// @param encrypting if true the cipher is initialised for
|
|
/// encryption, if false for decryption.
|
|
/// @param params the key and other data required by the cipher.
|
|
/// @exception IllegalArgumentException if the params argument is
|
|
/// inappropriate.
|
|
@override
|
|
void init(bool encrypting, CipherParameters? params) {
|
|
_encrypting = encrypting;
|
|
|
|
if (params is ParametersWithIV) {
|
|
var ivParam = params;
|
|
var iv = ivParam.iv;
|
|
|
|
if (iv.length < _iv.length) {
|
|
// prepend the supplied IV with zeros (per FIPS PUB 81)
|
|
var offset = _iv.length - iv.length;
|
|
_iv.fillRange(0, offset, 0);
|
|
_iv.setRange(offset, _iv.length, iv);
|
|
} else {
|
|
_iv.setRange(0, _iv.length, iv);
|
|
}
|
|
|
|
reset();
|
|
|
|
// if null it's an IV changed only.
|
|
if (ivParam.parameters != null) {
|
|
_underlyingCipher.init(true, ivParam.parameters);
|
|
}
|
|
} else {
|
|
reset();
|
|
_underlyingCipher.init(true, params);
|
|
}
|
|
}
|
|
|
|
/// Process one block of input from the array in and write it to
|
|
/// the out array.
|
|
///
|
|
/// @param in the array containing the input data.
|
|
/// @param inOff offset into the in array the data starts at.
|
|
/// @param out the array the output data will be copied into.
|
|
/// @param outOff the offset into the out array the output will start at.
|
|
/// @exception DataLengthException if there isn't enough data in in, or
|
|
/// space in out.
|
|
/// @exception IllegalStateException if the cipher isn't initialised.
|
|
/// @return the number of bytes processed and produced.
|
|
@override
|
|
int processBlock(Uint8List inp, int inpOff, Uint8List out, int outOff) =>
|
|
_encrypting
|
|
? _encryptBlock(inp, inpOff, out, outOff)
|
|
: _decryptBlock(inp, inpOff, out, outOff);
|
|
|
|
/// Do the appropriate processing for CFB mode encryption.
|
|
///
|
|
/// @param in the array containing the data to be encrypted.
|
|
/// @param inOff offset into the in array the data starts at.
|
|
/// @param out the array the encrypted data will be copied into.
|
|
/// @param outOff the offset into the out array the output will start at.
|
|
/// @exception DataLengthException if there isn't enough data in in, or
|
|
/// space in out.
|
|
/// @exception IllegalStateException if the cipher isn't initialised.
|
|
/// @return the number of bytes processed and produced.
|
|
int _encryptBlock(Uint8List inp, int inpOff, Uint8List out, int outOff) {
|
|
if ((inpOff + blockSize) > inp.length) {
|
|
throw ArgumentError('Input buffer too short');
|
|
}
|
|
|
|
if ((outOff + blockSize) > out.length) {
|
|
throw ArgumentError('Output buffer too short');
|
|
}
|
|
|
|
_underlyingCipher.processBlock(_cfbV!, 0, _cfbOutV!, 0);
|
|
|
|
// XOR the cfbV with the plaintext producing the ciphertext
|
|
for (var i = 0; i < blockSize; i++) {
|
|
out[outOff + i] = _cfbOutV![i] ^ inp[inpOff + i];
|
|
}
|
|
|
|
// change over the input block.
|
|
var offset = _cfbV!.length - blockSize;
|
|
_cfbV!.setRange(0, offset, _cfbV!.sublist(blockSize));
|
|
_cfbV!.setRange(offset, _cfbV!.length, out.sublist(outOff));
|
|
|
|
return blockSize;
|
|
}
|
|
|
|
/// Do the appropriate processing for CFB mode decryption.
|
|
///
|
|
/// @param in the array containing the data to be decrypted.
|
|
/// @param inOff offset into the in array the data starts at.
|
|
/// @param out the array the encrypted data will be copied into.
|
|
/// @param outOff the offset into the out array the output will start at.
|
|
/// @exception DataLengthException if there isn't enough data in in, or
|
|
/// space in out.
|
|
/// @exception IllegalStateException if the cipher isn't initialised.
|
|
/// @return the number of bytes processed and produced.
|
|
int _decryptBlock(Uint8List inp, int inpOff, Uint8List out, int outOff) {
|
|
if ((inpOff + blockSize) > inp.length) {
|
|
throw ArgumentError('Input buffer too short');
|
|
}
|
|
|
|
if ((outOff + blockSize) > out.length) {
|
|
throw ArgumentError('Output buffer too short');
|
|
}
|
|
|
|
_underlyingCipher.processBlock(_cfbV!, 0, _cfbOutV!, 0);
|
|
|
|
// change over the input block.
|
|
var offset = _cfbV!.length - blockSize;
|
|
_cfbV!.setRange(0, offset, _cfbV!.sublist(blockSize));
|
|
_cfbV!.setRange(offset, _cfbV!.length, inp.sublist(inpOff));
|
|
|
|
// XOR the cfbV with the ciphertext producing the plaintext
|
|
for (var i = 0; i < blockSize; i++) {
|
|
out[outOff + i] = _cfbOutV![i] ^ inp[inpOff + i];
|
|
}
|
|
|
|
return blockSize;
|
|
}
|
|
}
|