| // 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; |
| } |
| } |