blob: b89c9bbdc95caa2b0c0fe04a8c224ba5e1a50f28 [file] [log] [blame]
// See file LICENSE for more information.
library impl.stream_cipher.sic;
import 'dart:typed_data';
import 'package:pointycastle/api.dart';
import 'package:pointycastle/src/impl/base_stream_cipher.dart';
import 'package:pointycastle/src/ufixnum.dart';
import 'package:pointycastle/src/registry/registry.dart';
/// Implementation of SIC mode of operation as a [StreamCipher]. This implementation uses the IV as the initial nonce value and
/// keeps incrementing it by 1 for every block. The counter may overflow and rotate, and that would cause a two-time-pad
/// error, but this is so unlikely to happen for usual block sizes that we don't check for that event. It is the responsibility
/// of the caller to make sure the counter does not overflow.
class SICStreamCipher extends BaseStreamCipher {
/// Intended for internal use.
// ignore: non_constant_identifier_names
static final FactoryConfig factoryConfig = DynamicFactoryConfig.suffix(
StreamCipher,
'/SIC',
(_, final Match match) => () {
var digestName = match.group(1);
return SICStreamCipher(BlockCipher(digestName!));
});
final BlockCipher underlyingCipher;
late Uint8List _iv;
late Uint8List _counter;
late Uint8List _counterOut;
late int _consumed;
SICStreamCipher(this.underlyingCipher) {
_iv = Uint8List(underlyingCipher.blockSize);
_counter = Uint8List(underlyingCipher.blockSize);
_counterOut = Uint8List(underlyingCipher.blockSize);
}
@override
String get algorithmName => '${underlyingCipher.algorithmName}/SIC';
@override
void reset() {
underlyingCipher.reset();
_counter.setAll(0, _iv);
_counterOut.fillRange(0, _counterOut.length, 0);
_consumed = _counterOut.length;
}
@override
void init(bool forEncryption, covariant ParametersWithIV params) {
_iv.setAll(0, params.iv);
reset();
underlyingCipher.init(true, params.parameters);
}
@override
void processBytes(
Uint8List? inp, int inpOff, int len, Uint8List? out, int outOff) {
for (var i = 0; i < len; i++) {
out![outOff + i] = returnByte(inp![inpOff + i]);
}
}
@override
int returnByte(int inp) {
_feedCounterIfNeeded();
return clip8(inp) ^ _counterOut[_consumed++];
}
/// Calls [_feedCounter] if all [_counterOut] bytes have been consumed
void _feedCounterIfNeeded() {
if (_consumed >= _counterOut.length) {
_feedCounter();
}
}
// ignore: slash_for_doc_comments
/// Fills [_counterOut] with a value got from encrypting [_counter] with
/// the _underlyingCipher, resets [_consumed] to 0 and increments the
/// [_counter].
void _feedCounter() {
underlyingCipher.processBlock(_counter, 0, _counterOut, 0);
_incrementCounter();
_consumed = 0;
}
/// Increments [_counter] by 1
void _incrementCounter() {
int i;
for (i = _counter.lengthInBytes - 1; i >= 0; i--) {
var val = _counter[i];
val++;
_counter[i] = val;
if (_counter[i] != 0) break;
}
}
}