99 lines
3 KiB
Dart
99 lines
3 KiB
Dart
// 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;
|
||
}
|
||
}
|
||
}
|