// 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; } } }