214 lines
6 KiB
Dart
214 lines
6 KiB
Dart
// 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;
|
|
}
|
|
}
|