library impl.stream_cipher.rc4_engine; import 'dart:typed_data'; import 'package:pointycastle/api.dart'; import 'package:pointycastle/src/impl/base_stream_cipher.dart'; import 'package:pointycastle/src/registry/registry.dart'; class RC4Engine extends BaseStreamCipher { static final FactoryConfig factoryConfig = StaticFactoryConfig(StreamCipher, 'RC4', () => RC4Engine()); static int STATE_LENGTH = 256; Uint8List? _engineState; int _x = 0; int _y = 0; late Uint8List _workingKey; @override String get algorithmName => 'RC4'; @override void init(bool forEncryption, CipherParameters? params) { if (params != null) { if (params is KeyParameter) { _workingKey = params.key; setKey(params.key); } else { throw ArgumentError('Parameters of invalid type'); } } else { throw ArgumentError('Missing parameter'); } } @override Uint8List process(Uint8List data) { var out = Uint8List(data.length); processBytes(data, 0, data.length, out, 0); return out; } @override void processBytes( Uint8List inp, int inpOff, int len, Uint8List out, int outOff) { if ((inpOff + len) > inp.length) { throw ArgumentError('input buffer too short'); } if ((outOff + len) > out.length) { throw ArgumentError('output buffer too short'); } for (var i = 0; i < len; i++) { _x = (_x + 1) & 0xff; _y = (_engineState![_x] + _y) & 0xff; // swap var tmp = _engineState![_x]; _engineState![_x] = _engineState![_y]; _engineState![_y] = tmp; // xor out[i + outOff] = inp[i + inpOff] ^ _engineState![(_engineState![_x] + _engineState![_y]) & 0xff]; } } @override void reset() { setKey(_workingKey); } @override int returnByte(int inp) { _x = (_x + 1) & 0xff; _y = (_engineState![_x] + _y) & 0xff; // swap var tmp = _engineState![_x]; _engineState![_x] = _engineState![_y]; _engineState![_y] = tmp; // xor return inp ^ _engineState![(_engineState![_x] + _engineState![_y]) & 0xff]; } void setKey(Uint8List keyBytes) { _workingKey = keyBytes; _x = 0; _y = 0; _engineState ??= Uint8List(STATE_LENGTH); // reset the state of the engine for (var i = 0; i < STATE_LENGTH; i++) { _engineState![i] = i; } var i1 = 0; var i2 = 0; for (var i = 0; i < STATE_LENGTH; i++) { i2 = ((keyBytes[i1] & 0xff) + _engineState![i] + i2) & 0xff; // do the byte-swap inline var tmp = _engineState![i]; _engineState![i] = _engineState![i2]; _engineState![i2] = tmp; i1 = (i1 + 1) % keyBytes.length; } } }