| // See file LICENSE for more information. |
| |
| library impl.mac.cbc_block_cipher_mac; |
| |
| import 'dart:typed_data'; |
| |
| import 'package:pointycastle/api.dart'; |
| import 'package:pointycastle/src/registry/registry.dart'; |
| import 'package:pointycastle/src/impl/base_mac.dart'; |
| import 'package:pointycastle/block/modes/cbc.dart'; |
| |
| /// standard CBC Block Cipher MAC - if no padding is specified the default of |
| /// pad of zeroes is used. |
| class CBCBlockCipherMac extends BaseMac { |
| static final FactoryConfig factoryConfig = DynamicFactoryConfig.regex( |
| Mac, |
| r'^(.+)/CBC_CMAC(/(.+))?$', |
| (_, final Match match) => () { |
| var cipher = BlockCipher(match.group(1)!); |
| var padding = match.groupCount >= 3 && |
| match.group(3) != null && |
| match.group(3)!.isNotEmpty |
| ? Padding(match.group(3)!) |
| : null; |
| return CBCBlockCipherMac.fromCipherAndPadding(cipher, padding); |
| }, |
| ); |
| |
| late Uint8List _mac; |
| |
| late Uint8List _buf; |
| late int _bufOff; |
| final BlockCipher _cipher; |
| final Padding? _padding; |
| |
| final int _macSize; |
| |
| ParametersWithIV? _params; |
| |
| /// |
| /// create a standard MAC based on a CBC block cipher. This will produce an |
| /// authentication code half the length of the block size of the cipher. |
| /// |
| /// * [cipher] the cipher to be used as the basis of the MAC generation. |
| CBCBlockCipherMac.fromCipher(BlockCipher cipher) |
| : this(cipher, (cipher.blockSize * 8) ~/ 2, null); |
| |
| /// |
| /// create a standard MAC based on a CBC block cipher. This will produce an |
| /// authentication code half the length of the block size of the cipher. |
| /// |
| /// * [cipher] the cipher to be used as the basis of the MAC generation. |
| /// * [padding] the padding to be used to complete the last block. |
| CBCBlockCipherMac.fromCipherAndPadding(BlockCipher cipher, Padding? padding) |
| : this(cipher, (cipher.blockSize * 8) ~/ 2, padding); |
| |
| /// |
| /// create a standard MAC based on a block cipher with the size of the |
| /// MAC been given in bits. This class uses CBC mode as the basis for the |
| /// MAC generation. |
| /// |
| /// Note: the size of the MAC must be at least 24 bits (FIPS Publication 81), |
| /// or 16 bits if being used as a data authenticator (FIPS Publication 113), |
| /// and in general should be less than the size of the block cipher as it |
| /// reduces the chance of an exhaustive attack (see Handbook of Applied |
| /// Cryptography). |
| /// |
| /// * [cipher] the cipher to be used as the basis of the MAC generation. |
| /// * [macSizeInBits] the size of the MAC in bits, must be a multiple of 8. |
| CBCBlockCipherMac.fromCipherAndMacSize(BlockCipher cipher, int macSizeInBits) |
| : this(cipher, macSizeInBits, null); |
| |
| /// |
| /// create a standard MAC based on a block cipher with the size of the |
| /// MAC been given in bits. This class uses CBC mode as the basis for the |
| /// MAC generation. |
| /// |
| /// Note: the size of the MAC must be at least 24 bits (FIPS Publication 81), |
| /// or 16 bits if being used as a data authenticator (FIPS Publication 113), |
| /// and in general should be less than the size of the block cipher as it |
| /// reduces the chance of an exhaustive attack (see Handbook of Applied |
| /// Cryptography). |
| /// |
| /// * [cipher] the cipher to be used as the basis of the MAC generation. |
| /// * [macSizeInBits] the size of the MAC in bits, must be a multiple of 8. |
| /// * [padding] the padding to be used to complete the last block. |
| CBCBlockCipherMac(BlockCipher cipher, int macSizeInBits, Padding? padding) |
| : _cipher = CBCBlockCipher(cipher), |
| _macSize = macSizeInBits ~/ 8, |
| _padding = padding { |
| if ((macSizeInBits % 8) != 0) { |
| throw ArgumentError('MAC size must be multiple of 8'); |
| } |
| |
| _mac = Uint8List(cipher.blockSize); |
| |
| _buf = Uint8List(cipher.blockSize); |
| _bufOff = 0; |
| } |
| |
| @override |
| String get algorithmName { |
| var paddingName = _padding != null ? '/${_padding.algorithmName}' : ''; |
| return '${_cipher.algorithmName}_CMAC$paddingName'; |
| } |
| |
| @override |
| void init(CipherParameters params) { |
| if (params is ParametersWithIV) { |
| _params = params; |
| } else if (params is KeyParameter) { |
| final zeroIV = Uint8List(params.key.length); |
| _params = ParametersWithIV(params, zeroIV); |
| } |
| |
| reset(); |
| |
| _cipher.init(true, _params); |
| } |
| |
| @override |
| int get macSize => _macSize; |
| |
| @override |
| void updateByte(int inp) { |
| if (_bufOff == _buf.length) { |
| _cipher.processBlock(_buf, 0, _mac, 0); |
| _bufOff = 0; |
| } |
| |
| _buf[_bufOff++] = inp; |
| } |
| |
| @override |
| void update(Uint8List inp, int inOff, int len) { |
| if (len < 0) { |
| throw ArgumentError('Can\'t have a negative input length!'); |
| } |
| |
| var blockSize = _cipher.blockSize; |
| var gapLen = blockSize - _bufOff; |
| |
| if (len > gapLen) { |
| _buf.setRange(_bufOff, _bufOff + gapLen, inp.sublist(inOff)); |
| |
| _cipher.processBlock(_buf, 0, _mac, 0); |
| |
| _bufOff = 0; |
| len -= gapLen; |
| inOff += gapLen; |
| |
| while (len > blockSize) { |
| _cipher.processBlock(inp, inOff, _mac, 0); |
| |
| len -= blockSize; |
| inOff += blockSize; |
| } |
| } |
| |
| _buf.setRange(_bufOff, _bufOff + len, inp.sublist(inOff)); |
| |
| _bufOff += len; |
| } |
| |
| /// Reset the mac generator. |
| @override |
| void reset() { |
| // clean the buffer. |
| for (var i = 0; i < _buf.length; i++) { |
| _buf[i] = 0; |
| } |
| |
| _bufOff = 0; |
| |
| // reset the underlying cipher. |
| _cipher.reset(); |
| |
| _cipher.init(true, _params); |
| |
| if (_params != null) { |
| _cipher.init(true, _params); |
| } |
| } |
| |
| @override |
| int doFinal(Uint8List out, int outOff) { |
| var blockSize = _cipher.blockSize; |
| |
| if (_padding == null) { |
| // |
| // pad with zeroes |
| // |
| while (_bufOff < blockSize) { |
| _buf[_bufOff] = 0; |
| _bufOff++; |
| } |
| } else { |
| if (_bufOff == blockSize) { |
| _cipher.processBlock(_buf, 0, _mac, 0); |
| _bufOff = 0; |
| } |
| |
| _padding.addPadding(_buf, _bufOff); |
| } |
| |
| _cipher.processBlock(_buf, 0, _mac, 0); |
| |
| out.setRange(outOff, outOff + _macSize, _mac); |
| |
| reset(); |
| |
| return _macSize; |
| } |
| } |