| // See file LICENSE for more information. |
| |
| library impl.block_cipher.modes.gctr; |
| |
| import 'dart:typed_data'; |
| |
| import 'package:pointycastle/api.dart'; |
| import 'package:pointycastle/src/impl/base_block_cipher.dart'; |
| import 'package:pointycastle/src/registry/registry.dart'; |
| import 'package:pointycastle/src/ufixnum.dart'; |
| |
| /// Implementation of GOST 28147 OFB counter mode (GCTR) on top of a [BlockCipher]. |
| class GCTRBlockCipher extends BaseBlockCipher { |
| /// Intended for internal use. |
| static final FactoryConfig factoryConfig = DynamicFactoryConfig.suffix( |
| BlockCipher, |
| '/GCTR', |
| (_, final Match match) => () { |
| var underlying = BlockCipher(match.group(1)!); |
| return GCTRBlockCipher(underlying); |
| }); |
| |
| static const C1 = 16843012; //00000001000000010000000100000100 |
| static const C2 = 16843009; //00000001000000010000000100000001 |
| |
| final BlockCipher _underlyingCipher; |
| |
| late Uint8List _iv; |
| Uint8List? _ofbV; |
| Uint8List? _ofbOutV; |
| |
| bool _firstStep = true; |
| late int _n3; |
| late int _n4; |
| |
| GCTRBlockCipher(this._underlyingCipher) { |
| if (blockSize != 8) { |
| throw ArgumentError('GCTR can only be used with 64 bit block ciphers'); |
| } |
| |
| _iv = Uint8List(_underlyingCipher.blockSize); |
| _ofbV = Uint8List(_underlyingCipher.blockSize); |
| _ofbOutV = Uint8List(_underlyingCipher.blockSize); |
| } |
| |
| @override |
| int get blockSize => _underlyingCipher.blockSize; |
| @override |
| String get algorithmName => '${_underlyingCipher.algorithmName}/GCTR'; |
| @override |
| void reset() { |
| _ofbV!.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. //ignored by this CTR mode |
| /// @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) { |
| _firstStep = true; |
| _n3 = 0; |
| _n4 = 0; |
| |
| 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 params is null we reuse the current working key. |
| if (ivParam.parameters != null) { |
| _underlyingCipher.init(true, ivParam.parameters); |
| } |
| } else { |
| // TODO: make this behave in a standard way (as the other modes of operation) |
| reset(); |
| |
| // if params is null we reuse the current working key. |
| if (params != null) { |
| _underlyingCipher.init(true, params); |
| } |
| } |
| } |
| |
| @override |
| int processBlock(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'); |
| } |
| |
| if (_firstStep) { |
| _firstStep = false; |
| _underlyingCipher.processBlock(_ofbV!, 0, _ofbOutV!, 0); |
| _n3 = _bytesToint(_ofbOutV, 0); |
| _n4 = _bytesToint(_ofbOutV, 4); |
| } |
| _n3 += C2; |
| _n4 += C1; |
| _intTobytes(_n3, _ofbV, 0); |
| _intTobytes(_n4, _ofbV, 4); |
| |
| _underlyingCipher.processBlock(_ofbV!, 0, _ofbOutV!, 0); |
| |
| // XOR the ofbV with the plaintext producing the cipher text (and the next input block). |
| for (var i = 0; i < blockSize; i++) { |
| out[outOff + i] = _ofbOutV![i] ^ inp[inpOff + i]; |
| } |
| |
| // change over the input block. |
| var offset = _ofbV!.length - blockSize; |
| _ofbV!.setRange(0, offset, _ofbV!.sublist(blockSize)); |
| _ofbV!.setRange(offset, _ofbV!.length, _ofbOutV!); |
| |
| return blockSize; |
| } |
| |
| int _bytesToint(Uint8List? inp, int inpOff) { |
| return unpack32(inp, inpOff, Endian.little); |
| } |
| |
| void _intTobytes(int num, Uint8List? out, int outOff) { |
| pack32(num, out, outOff, Endian.little); |
| } |
| } |