315 lines
7.2 KiB
Dart
315 lines
7.2 KiB
Dart
library impl.stream_cipher.chacha7539;
|
|
|
|
import 'dart:typed_data';
|
|
|
|
import '../export.dart';
|
|
import '../api.dart';
|
|
import '../src/impl/base_stream_cipher.dart';
|
|
import '../src/registry/registry.dart';
|
|
import '../src/ufixnum.dart';
|
|
|
|
// ignore_for_file: omit_local_variable_types, prefer_single_quotes
|
|
// ignore_for_file: non_constant_identifier_names, directives_ordering
|
|
// ignore_for_file: prefer_typing_uninitialized_variables, camel_case_types
|
|
// ignore_for_file: annotate_overrides
|
|
|
|
/// RFC version of Daniel J. Bernstein's ChaCha20. This uses a 12 byte IV, among
|
|
/// other changes.
|
|
class ChaCha7539Engine extends BaseStreamCipher {
|
|
static final FactoryConfig factoryConfig = DynamicFactoryConfig.prefix(
|
|
StreamCipher,
|
|
'ChaCha7539/',
|
|
(_, final Match match) => () {
|
|
var rounds = int.parse(match.group(1)!);
|
|
return ChaCha7539Engine.fromRounds(rounds);
|
|
});
|
|
|
|
ChaCha7539Engine() {
|
|
rounds = 20;
|
|
}
|
|
|
|
ChaCha7539Engine.fromRounds(this.rounds);
|
|
|
|
int rounds = 20;
|
|
|
|
static const STATE_SIZE = 16;
|
|
|
|
static final _sigma = Uint8List.fromList([
|
|
101,
|
|
120,
|
|
112,
|
|
97,
|
|
110,
|
|
100,
|
|
32,
|
|
51,
|
|
50,
|
|
45,
|
|
98,
|
|
121,
|
|
116,
|
|
101,
|
|
32,
|
|
107
|
|
]);
|
|
static final _tau = Uint8List.fromList([
|
|
101,
|
|
120,
|
|
112,
|
|
97,
|
|
110,
|
|
100,
|
|
32,
|
|
49,
|
|
54,
|
|
45,
|
|
98,
|
|
121,
|
|
116,
|
|
101,
|
|
32,
|
|
107
|
|
]);
|
|
|
|
Uint8List? _workingKey;
|
|
late Uint8List _workingIV;
|
|
|
|
final _state = List<int>.filled(STATE_SIZE, 0, growable: false);
|
|
final _buffer = List<int>.filled(STATE_SIZE, 0, growable: false);
|
|
|
|
final _keyStream = Uint8List(STATE_SIZE * 4);
|
|
var _keyStreamOffset = 0;
|
|
|
|
var _initialised = false;
|
|
|
|
@override
|
|
String get algorithmName => 'ChaCha7539/$rounds';
|
|
|
|
@override
|
|
void reset() {
|
|
_state[12] = 0;
|
|
if (_workingKey != null) {
|
|
_setKey(_workingKey!, _workingIV);
|
|
}
|
|
}
|
|
|
|
@override
|
|
void init(
|
|
bool forEncryption, covariant ParametersWithIV<KeyParameter> params) {
|
|
var uparams = params.parameters;
|
|
var iv = params.iv;
|
|
if (iv.length != 12) {
|
|
throw ArgumentError('ChaCha20-7539 requires exactly 12 bytes of IV');
|
|
}
|
|
|
|
_workingIV = iv;
|
|
_workingKey = uparams!.key;
|
|
|
|
_setKey(_workingKey!, _workingIV);
|
|
}
|
|
|
|
@override
|
|
int returnByte(int inp) {
|
|
if (_keyStreamOffset == 0) {
|
|
generateKeyStream(_keyStream);
|
|
|
|
if (++_state[12] == 0) {
|
|
++_state[13];
|
|
}
|
|
}
|
|
|
|
var out = clip8(_keyStream[_keyStreamOffset] ^ inp);
|
|
_keyStreamOffset = (_keyStreamOffset + 1) & 63;
|
|
|
|
return out;
|
|
}
|
|
|
|
@override
|
|
void processBytes(
|
|
Uint8List inp, int inpOff, int len, Uint8List out, int outOff) {
|
|
if (!_initialised) {
|
|
throw StateError('ChaCha20 not initialized: please call init() first');
|
|
}
|
|
|
|
if ((inpOff + len) > inp.length) {
|
|
throw ArgumentError(
|
|
'Input buffer too short or requested length too long');
|
|
}
|
|
|
|
if ((outOff + len) > out.length) {
|
|
throw ArgumentError(
|
|
'Output buffer too short or requested length too long');
|
|
}
|
|
|
|
for (var i = 0; i < len; i++) {
|
|
if (_keyStreamOffset == 0) {
|
|
generateKeyStream(_keyStream);
|
|
|
|
if (++_state[12] == 0) throw StateError('Illegal increase of counter');
|
|
}
|
|
|
|
out[i + outOff] = clip8(_keyStream[_keyStreamOffset] ^ inp[i + inpOff]);
|
|
_keyStreamOffset = (_keyStreamOffset + 1) & 63;
|
|
}
|
|
}
|
|
|
|
void _setKey(Uint8List keyBytes, Uint8List ivBytes) {
|
|
_workingKey = keyBytes;
|
|
_workingIV = ivBytes;
|
|
|
|
_keyStreamOffset = 0;
|
|
Uint8List constants;
|
|
|
|
if (_workingKey!.length == 32) {
|
|
constants = _sigma;
|
|
} else {
|
|
constants = _tau;
|
|
}
|
|
|
|
//Key
|
|
_state[4] = unpack32(_workingKey, 0, Endian.little);
|
|
_state[5] = unpack32(_workingKey, 4, Endian.little);
|
|
_state[6] = unpack32(_workingKey, 8, Endian.little);
|
|
_state[7] = unpack32(_workingKey, 12, Endian.little);
|
|
_state[8] = unpack32(_workingKey, 16, Endian.little);
|
|
_state[9] = unpack32(_workingKey, 20, Endian.little);
|
|
_state[10] = unpack32(_workingKey, 24, Endian.little);
|
|
_state[11] = unpack32(_workingKey, 28, Endian.little);
|
|
|
|
_state[0] = unpack32(constants, 0, Endian.little);
|
|
_state[1] = unpack32(constants, 4, Endian.little);
|
|
_state[2] = unpack32(constants, 8, Endian.little);
|
|
_state[3] = unpack32(constants, 12, Endian.little);
|
|
|
|
_state[12] = 0;
|
|
|
|
// IV
|
|
var off = 0;
|
|
for (var i = 0; i < 3; ++i) {
|
|
_state[13 + i] = unpack32(_workingIV, off, Endian.little);
|
|
off += 4;
|
|
}
|
|
|
|
_initialised = true;
|
|
}
|
|
|
|
void generateKeyStream(Uint8List output) {
|
|
_core(rounds, _state, _buffer);
|
|
var outOff = 0;
|
|
for (var x in _buffer) {
|
|
pack32(x, output, outOff, Endian.little);
|
|
outOff += 4;
|
|
}
|
|
}
|
|
|
|
/// The ChaCha20 core function
|
|
void _core(int rounds, List<int> input, List<int> x) {
|
|
var x00 = input[0];
|
|
var x01 = input[1];
|
|
var x02 = input[2];
|
|
var x03 = input[3];
|
|
var x04 = input[4];
|
|
var x05 = input[5];
|
|
var x06 = input[6];
|
|
var x07 = input[7];
|
|
var x08 = input[8];
|
|
var x09 = input[9];
|
|
var x10 = input[10];
|
|
var x11 = input[11];
|
|
var x12 = input[12];
|
|
var x13 = input[13];
|
|
var x14 = input[14];
|
|
var x15 = input[15];
|
|
|
|
for (var i = rounds; i > 0; i -= 2) {
|
|
x00 += x04;
|
|
x12 = crotl32(x12 ^ x00, 16);
|
|
x08 += x12;
|
|
x04 = crotl32(x04 ^ x08, 12);
|
|
x00 += x04;
|
|
x12 = crotl32(x12 ^ x00, 8);
|
|
x08 += x12;
|
|
x04 = crotl32(x04 ^ x08, 7);
|
|
x01 += x05;
|
|
x13 = crotl32(x13 ^ x01, 16);
|
|
x09 += x13;
|
|
x05 = crotl32(x05 ^ x09, 12);
|
|
x01 += x05;
|
|
x13 = crotl32(x13 ^ x01, 8);
|
|
x09 += x13;
|
|
x05 = crotl32(x05 ^ x09, 7);
|
|
x02 += x06;
|
|
x14 = crotl32(x14 ^ x02, 16);
|
|
x10 += x14;
|
|
x06 = crotl32(x06 ^ x10, 12);
|
|
x02 += x06;
|
|
x14 = crotl32(x14 ^ x02, 8);
|
|
x10 += x14;
|
|
x06 = crotl32(x06 ^ x10, 7);
|
|
x03 += x07;
|
|
x15 = crotl32(x15 ^ x03, 16);
|
|
x11 += x15;
|
|
x07 = crotl32(x07 ^ x11, 12);
|
|
x03 += x07;
|
|
x15 = crotl32(x15 ^ x03, 8);
|
|
x11 += x15;
|
|
x07 = crotl32(x07 ^ x11, 7);
|
|
x00 += x05;
|
|
x15 = crotl32(x15 ^ x00, 16);
|
|
x10 += x15;
|
|
x05 = crotl32(x05 ^ x10, 12);
|
|
x00 += x05;
|
|
x15 = crotl32(x15 ^ x00, 8);
|
|
x10 += x15;
|
|
x05 = crotl32(x05 ^ x10, 7);
|
|
x01 += x06;
|
|
x12 = crotl32(x12 ^ x01, 16);
|
|
x11 += x12;
|
|
x06 = crotl32(x06 ^ x11, 12);
|
|
x01 += x06;
|
|
x12 = crotl32(x12 ^ x01, 8);
|
|
x11 += x12;
|
|
x06 = crotl32(x06 ^ x11, 7);
|
|
x02 += x07;
|
|
x13 = crotl32(x13 ^ x02, 16);
|
|
x08 += x13;
|
|
x07 = crotl32(x07 ^ x08, 12);
|
|
x02 += x07;
|
|
x13 = crotl32(x13 ^ x02, 8);
|
|
x08 += x13;
|
|
x07 = crotl32(x07 ^ x08, 7);
|
|
x03 += x04;
|
|
x14 = crotl32(x14 ^ x03, 16);
|
|
x09 += x14;
|
|
x04 = crotl32(x04 ^ x09, 12);
|
|
x03 += x04;
|
|
x14 = crotl32(x14 ^ x03, 8);
|
|
x09 += x14;
|
|
x04 = crotl32(x04 ^ x09, 7);
|
|
}
|
|
var xup = [
|
|
x00,
|
|
x01,
|
|
x02,
|
|
x03,
|
|
x04,
|
|
x05,
|
|
x06,
|
|
x07,
|
|
x08,
|
|
x09,
|
|
x10,
|
|
x11,
|
|
x12,
|
|
x13,
|
|
x14,
|
|
x15
|
|
];
|
|
for (var i = 0; i < STATE_SIZE; ++i) {
|
|
x[i] = csum32(xup[i], input[i]);
|
|
}
|
|
}
|
|
|
|
@override
|
|
dynamic noSuchMethod(Invocation invocation);
|
|
}
|