// See file LICENSE for more information. library impl.secure_random.auto_seed_block_ctr_random; import 'dart:typed_data'; import 'package:pointycastle/api.dart'; import 'package:pointycastle/random/block_ctr_random.dart'; import 'package:pointycastle/src/registry/registry.dart'; /// An implementation of [SecureRandom] that uses a [BlockCipher] with CTR mode to generate random /// values and automatically self reseeds itself after each request for data, in order to achieve /// forward security. See section 4.1 of the paper: /// Practical Random Number Generation in Software (by John Viega). class AutoSeedBlockCtrRandom implements SecureRandom { /// Intended for internal use. static final FactoryConfig factoryConfig = DynamicFactoryConfig.regex( SecureRandom, r'^(.*)/CTR/AUTO-SEED-PRNG$', (_, final Match match) => () { var blockCipherName = match.group(1); var blockCipher = BlockCipher(blockCipherName!); return AutoSeedBlockCtrRandom(blockCipher); }); late BlockCtrRandom _delegate; final bool _reseedIV; var _inAutoReseed = false; late int _autoReseedKeyLength; @override String get algorithmName => '${_delegate.cipher.algorithmName}/CTR/AUTO-SEED-PRNG'; AutoSeedBlockCtrRandom(BlockCipher cipher, [this._reseedIV = true]) { _delegate = BlockCtrRandom(cipher); } @override void seed(CipherParameters params) { if (params is ParametersWithIV) { _autoReseedKeyLength = params.parameters!.key.length; _delegate.seed(params); } else if (params is KeyParameter) { _autoReseedKeyLength = params.key.length; _delegate.seed(params); } else { throw ArgumentError( 'Only types ParametersWithIV or KeyParameter allowed for seeding'); } } @override int nextUint8() => _autoReseedIfNeededAfter(() { return _delegate.nextUint8(); }); @override int nextUint16() => _autoReseedIfNeededAfter(() { return _delegate.nextUint16(); }); @override int nextUint32() => _autoReseedIfNeededAfter(() { return _delegate.nextUint32(); }); @override BigInt nextBigInteger(int bitLength) => _autoReseedIfNeededAfter(() { return _delegate.nextBigInteger(bitLength); }); @override Uint8List nextBytes(int count) => _autoReseedIfNeededAfter(() { return _delegate.nextBytes(count); }); dynamic _autoReseedIfNeededAfter(dynamic closure) { if (_inAutoReseed) { return closure(); } else { _inAutoReseed = true; var ret = closure(); _doAutoReseed(); _inAutoReseed = false; return ret; } } void _doAutoReseed() { var newKey = nextBytes(_autoReseedKeyLength); var keyParam = KeyParameter(newKey); CipherParameters params; if (_reseedIV) { params = ParametersWithIV(keyParam, nextBytes(_delegate.cipher.blockSize)); } else { params = keyParam; } _delegate.seed(params); } }