105 lines
3 KiB
Dart
105 lines
3 KiB
Dart
// 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<KeyParameter>) {
|
||
_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<KeyParameter> 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);
|
||
}
|
||
}
|