// See file LICENSE for more information. library impl.padded_block_cipher.padded_block_cipher_impl; import 'dart:typed_data'; import 'package:pointycastle/api.dart'; import 'package:pointycastle/src/registry/registry.dart'; /// The standard implementation of [PaddedBlockCipher]. class PaddedBlockCipherImpl implements PaddedBlockCipher { /// Intended for internal use. static final FactoryConfig factoryConfig = DynamicFactoryConfig.regex( PaddedBlockCipher, r'^(.+)/([^/]+)$', (_, final Match match) => () { var padding = Padding(match.group(2)!); var underlyingCipher = BlockCipher(match.group(1)!); return PaddedBlockCipherImpl(padding, underlyingCipher); }); @override final Padding padding; @override final BlockCipher cipher; bool? _encrypting; PaddedBlockCipherImpl(this.padding, this.cipher); @override String get algorithmName => '${cipher.algorithmName}/${padding.algorithmName}'; @override int get blockSize => cipher.blockSize; @override void reset() { _encrypting = null; cipher.reset(); } @override void init(bool forEncryption, covariant PaddedBlockCipherParameters params) { _encrypting = forEncryption; cipher.init(forEncryption, params.underlyingCipherParameters); padding.init(params.paddingCipherParameters); } @override Uint8List process(Uint8List data) { var inputBlocks = (data.length + blockSize - 1) ~/ blockSize; int outputBlocks; if (_encrypting ?? false) { outputBlocks = (data.length + blockSize) ~/ blockSize; } else { if ((data.length % blockSize) != 0) { throw ArgumentError( 'Input data length must be a multiple of cipher\'s block size'); } outputBlocks = inputBlocks; } var out = Uint8List(outputBlocks * blockSize); for (var i = 0; i < (inputBlocks - 1); i++) { var offset = i * blockSize; processBlock(data, offset, out, offset); } var lastBlockOffset = (inputBlocks - 1) * blockSize; var lastBlockSize = doFinal(data, lastBlockOffset, out, lastBlockOffset); return out.sublist(0, lastBlockOffset + lastBlockSize); } @override int processBlock(Uint8List inp, int inpOff, Uint8List out, int outOff) { return cipher.processBlock(inp, inpOff, out, outOff); } @override int doFinal(Uint8List inp, int inpOff, Uint8List out, int outOff) { if (_encrypting ?? false) { var lastInputBlock = Uint8List(blockSize)..setAll(0, inp.sublist(inpOff)); var remainder = inp.length - inpOff; if (remainder < blockSize) { // Padding goes embedded in last block of data padding.addPadding(lastInputBlock, inp.length - inpOff); processBlock(lastInputBlock, 0, out, outOff); return blockSize; } else { // Padding goes alone in an additional block processBlock(inp, inpOff, out, outOff); padding.addPadding(lastInputBlock, 0); processBlock(lastInputBlock, 0, out, outOff + blockSize); return 2 * blockSize; } } else { // Decrypt last block and remove padding processBlock(inp, inpOff, out, outOff); var padCount = padding.padCount(out.sublist(outOff)); var padOffsetInBlock = blockSize - padCount; out.fillRange(outOff + padOffsetInBlock, out.length, 0); return padOffsetInBlock; } } }