private void RecoverSeedPowerOf2Callback(RecoverSeedEventArgs args) { if (args.EventType == RecoverSeedEventType.SeedDiscovered) { args.Seed = PreviousState(args.Seed ^ _Multiplier) ^ _Multiplier; } this._OriginalCallback(args); }
private void RecoverSeedCallback(RecoverSeedEventArgs args) { if (args.EventType == RecoverSeedEventType.SeedDiscovered) { args.Seed = PreviousState(unchecked ((uint)args.Seed)); } this._OriginalCallback(args); }
/// <summary> /// Discovers the seed or seeds that produce the supplied PRNG output sequence. /// </summary> /// <param name="seedStart">The first seed value to test.</param> /// <param name="seedEnd">The last seed value that could be tested.</param> /// <param name="seedIncrement">A positive integer specifying the interval at which to test seeds. /// A value of 1 tests each seed serially, a value of 2 tests every other seed, and so on.</param> /// <param name="output">A sequence containing one or more PRNG output values (modulo <paramref name="limit"/>) /// and any number of instances of <paramref name="wildcard"/>.</param> /// <param name="limit">The lowest positive integer greater than the maximum allowed output value, or 0 if no limit was imposed on the output.</param> /// <param name="wildcard">A number which will match any PRNG output value when encountered in <paramref name="output"/>.</param> /// <param name="callback">A method to invoke with seed discovery or progress notifications.</param> /// <param name="progressInterval">The number of seeds to test between progress notifications, or 0 to not receive progress notifications.</param> /// <returns>true if seed discovery completed (even if no seed was discovered), or false if the callback canceled discovery or an error occurred.</returns> /// <remarks>If progress notifications are requested, <paramref name="callback"/> will be invoked for a final progress notification event before true is returned.</remarks> protected virtual bool RecoverSeed(ulong seedStart, ulong seedEnd, ulong seedIncrement, ulong[] output, ulong limit, ulong wildcard, RecoverSeedCallback callback, ulong progressInterval) { if (seedStart > seedEnd || seedIncrement == 0) return false; if (callback == null) progressInterval = 0; ulong totalcount = unchecked((seedEnd - seedStart) / seedIncrement + 1); // count = 0 (2**64) if minimum seed = 0, maxseed = 2**64 - 1, and seedIncrement = 1 ulong totalcountdown = totalcount; RecoverSeedEventArgs args = new RecoverSeedEventArgs(); ulong seed = seedStart; do { ulong countdown = ((totalcountdown < progressInterval || progressInterval == 0) ? totalcountdown : progressInterval); totalcountdown = unchecked(totalcountdown - countdown); // allow integer underflow so that 0 == 2**64 do { this.Seed(seed); int i; if (limit != 0) { for (i = 0; i < output.Length; i++) { if (this.Next(limit) != output[i] && output[i] != wildcard) break; } } else { for (i = 0; i < output.Length; i++) { if (this.Next() != output[i] && output[i] != wildcard) break; } } if (i == output.Length) // invoke callback for seed discovery notification { args.EventType = RecoverSeedEventType.SeedDiscovered; args.Seed = seed; args.CurrentAttempts = (totalcount - totalcountdown - countdown); args.TotalAttempts = totalcount; callback(args); if (args.Cancel) return false; } seed += seedIncrement; } while (unchecked(--countdown) != 0); // allow integer underflow here, so that 0 is equivalent to 2**64 if (progressInterval != 0) // invoke callback for progress notification { args.EventType = RecoverSeedEventType.ProgressNotification; args.Seed = seed; args.CurrentAttempts = (totalcount - totalcountdown - countdown); args.TotalAttempts = totalcount; callback(args); if (args.Cancel) return false; } } while (totalcountdown != 0); return true; }
private void RecoverSeedCallback(RecoverSeedEventArgs args) { if (args.EventType == RecoverSeedEventType.SeedDiscovered) args.Seed = PreviousState(unchecked((uint)args.Seed)); this._OriginalCallback(args); }
private void RecoverSeedPowerOf2Callback(RecoverSeedEventArgs args) { if (args.EventType == RecoverSeedEventType.SeedDiscovered) args.Seed = PreviousState(args.Seed ^ _Multiplier) ^ _Multiplier; this._OriginalCallback(args); }
protected override bool RecoverSeed(ulong seedStart, ulong seedEnd, ulong seedIncrement, ulong[] output, ulong limit, ulong wildcard, RecoverSeedCallback callback, ulong progressInterval) { if (output.Length < 2 || output[0] > (ulong.MaxValue / _OutputDivisor) || output[0] == wildcard || limit == 0 || limit > _OutputDivisor) return base.RecoverSeed(seedStart, seedEnd, seedIncrement, output, limit, wildcard, callback, progressInterval); if (seedStart > seedEnd || seedIncrement == 0) return false; if (unchecked(limit & (limit - 1)) == 0) // special handling for power-of-two limits { return this.RecoverSeedPowerOf2(seedStart, seedEnd, seedIncrement, output, limit, wildcard, callback, progressInterval); } RecoverSeedEventArgs args = new RecoverSeedEventArgs(); unchecked // disable arithmetic checks for performance { // operate on output suffix of length N - 1 so we can fix middle portion of seed to output[0] ulong[] suboutput = new ulong[output.Length - 1]; Array.Copy(output, 1, suboutput, 0, output.Length - 1); ulong seedtopincr = (uint)limit * _DiscardDivisorForMod; // Modulus is a power of 2, so GCD(limit, Modulus) is greatest power-of-two factor of limit ulong subgenoutputmask = ((uint)limit ^ ((uint)limit - 1)) >> 1; // fix bottom portion by brute-forcing subgenerator ulong subseed, subseedend; for (subseed = (output[0] & subgenoutputmask) * _DiscardDivisorForMod, subseedend = subseed + _DiscardDivisorForMod - 1; subseed != subseedend; subseed++) { if (subgenoutputmask != 0) // if limit is odd, we can't check this subgenerator seed against LSB(s) of output; we must test all possibilities below { this._LcgState = subseed; // don't use Seed() because it XORs by _Multiplier int i; for (i = 0; i < suboutput.Length; i++) { if ((this.Next(limit) & subgenoutputmask) != (suboutput[i] & subgenoutputmask) && suboutput[i] != wildcard) break; } if (i < suboutput.Length) continue; } // MSB(s) of subseed will contain LSB(s) of output[0], so | instead of + to keep duplicate bit(s) from interfering for (ulong seed = (output[0] * _DiscardDivisorForMod) | subseed; seed <= seedEnd; seed += seedtopincr) { this._LcgState = seed; // don't use Seed() because it XORs by _Multiplier int i; for (i = 0; i < suboutput.Length; i++) { if (this.Next(limit) != suboutput[i] && suboutput[i] != wildcard) break; } if (i == suboutput.Length) // invoke callback for seed discovery notification { args.EventType = RecoverSeedEventType.SeedDiscovered; args.Seed = (PreviousState(seed) ^ _Multiplier); // TO-DO TODO: estimate progress/total somehow? args.CurrentAttempts = 0; args.TotalAttempts = 0; callback(args); if (args.Cancel) return false; } } //for(seed<=seedEnd) } //for(subseed<subgensize) } //unchecked return true; }
protected override bool RecoverSeed(ulong seedStart, ulong seedEnd, ulong seedIncrement, ulong[] output, ulong limit, ulong wildcard, RecoverSeedCallback callback, ulong progressInterval) { if (output.Length < 2 || output[0] > (ulong.MaxValue / _OutputDivisor) || output[0] == wildcard || limit == 0 || limit > _OutputDivisor) return base.RecoverSeed(seedStart, seedEnd, seedIncrement, output, limit, wildcard, callback, progressInterval); if (seedStart > seedEnd || seedIncrement == 0) return false; unchecked // disable arithmetic checks for performance { // operate on output suffix of length N - 1 so we can fix top portion of seed to output[0] ulong[] suboutput = new ulong[output.Length - 1]; Array.Copy(output, 1, suboutput, 0, output.Length - 1); ulong rangestart = ((_OutputDivisor * output[0]) + (uint)limit - 1) / (uint)limit; // add (limit - 1) to round up, to make sure 'rangestart' will produce output[0] rangestart *= _DiscardDivisor; ulong rangeend = (((_OutputDivisor * (output[0] + 1)) + (uint)limit - 1) / (uint)limit) - 1; // lowest seed that produces (output[0] + 1), then - 1 rangeend = (rangeend * _DiscardDivisor) + (_DiscardDivisor - 1); if (rangestart <= seedStart) { rangestart = seedStart; } else if (seedIncrement > 1) { ulong incroffset = (rangestart - seedStart) % seedIncrement; if (incroffset != 0) rangestart += (seedIncrement - incroffset); // advance to the next multiple of 'seedIncrement' (relative to 'seedStart') } if (rangeend > seedEnd) rangeend = seedEnd; ulong output1 = ((output.Length > 2) ? output[1] : wildcard); uint blocksize = (uint)(_Modulus / ((ulong)_Multiplier * limit)); if (blocksize < 2) output1 = wildcard; // don't attempt improved attack if block size is too small if (output1 == wildcard || seedIncrement != 1) { this._OriginalCallback = callback; return base.RecoverSeed(rangestart, rangeend, seedIncrement, suboutput, limit, wildcard, this.RecoverSeedCallback, progressInterval); } // otherwise, use second output to skip blocks of candidate states RecoverSeedEventArgs args = new RecoverSeedEventArgs(); uint offset = NextFromState((uint)rangestart, (uint)limit); // determine how far off current output is from second output offset = (uint)output1 + ((output1 < offset) ? (uint)limit : 0) - offset; if (offset >= 2) // skip ahead conservatively to vicinity of a block of consecutive states that will produce 'output1' rangestart += (offset - 1) * blocksize; while (NextFromState((uint)rangestart, (uint)limit) != output1) // TO-DO TODO: binary seek, starting at +'blocksize' rangestart++; uint blockstart = (uint)rangestart; uint blockend; for (; ; ) { blockend = blockstart + blocksize; // [blockstart..blockend] is (blocksize + 1) states, to accommodate rounding variations if (blockend >= rangeend) blockend = (uint)rangeend; for (uint state = blockstart; ; state++) { this.Seed(state); int i; for (i = 0; i < suboutput.Length; i++) { if (this.Next(limit) != suboutput[i] && suboutput[i] != wildcard) break; } if (i == suboutput.Length) // invoke callback for seed discovery notification { args.EventType = RecoverSeedEventType.SeedDiscovered; args.Seed = PreviousState(state); // TO-DO TODO: estimate progress/total somehow? args.CurrentAttempts = 0; args.TotalAttempts = 0; callback(args); if (args.Cancel) return false; } if (state == blockend) // check only after body of loop so that 'blockend' will get tested break; } //for(state) blockstart += ((uint)limit * blocksize); if (blockstart >= rangeend) break; // 'blockend' will be set at the top of the loop body while (NextFromState(blockstart, (uint)limit) != output1) // TO-DO TODO: binary seek? blockstart++; } //for(;;) } //unchecked return true; }
protected override bool RecoverSeed(ulong seedStart, ulong seedEnd, ulong seedIncrement, ulong[] output, ulong limit, ulong wildcard, RecoverSeedCallback callback, ulong progressInterval) { if (output.Length < 2 || output[0] >= limit || output[0] == wildcard || limit == 0 || limit > _Modulus || seedIncrement != 1) return base.RecoverSeed(seedStart, seedEnd, seedIncrement, output, limit, wildcard, callback, progressInterval); if (seedStart < this.MinimumSeed) seedStart = this.MinimumSeed; if (seedEnd > this.MaximumSeed) seedEnd = this.MaximumSeed; if (seedStart > seedEnd) return false; ulong output1 = ((output.Length > 2) ? output[1] : wildcard); uint blocksize = (uint)(_Modulus / ((ulong)_Multiplier1 * limit)); if (blocksize < 2) output1 = wildcard; // don't attempt improved attack if block size is too small RecoverSeedEventArgs args = new RecoverSeedEventArgs(); unchecked // disable arithmetic checks for performance { // operate on output suffix of length N - 1 so we can fix bottom portion of seed to output[0] ulong[] suboutput = new ulong[output.Length - 1]; Array.Copy(output, 1, suboutput, 0, output.Length - 1); // 2147483589.46728 rounded down is 2147483589 and rounded up is 2147483590; rounding outward ensures we won't exclude a viable candidate state uint difflo = (uint)(((2147483589 * output[0]) + limit - 1) / limit); // add (limit - 1) to round up, to make sure 'difflo' will (almost always) produce output[0] uint diffhi = (uint)(((2147483590 * (output[0] + 1)) + limit - 1) / limit) - 1; // lowest difference that produces (output[0] + 1), then - 1 for (uint state0 = 1; state0 < _Modulus; state0++) { uint state1start = (state0 + _Modulus - diffhi); // State1 such that (State0 - State1) == diffhi; subtract 'diffhi' to get State1 lower bound if (state1start >= _Modulus) // _Modulus rather than _Modulus1 because difference is computed modulo _Modulus state1start -= _Modulus; if (state1start == 0 || state1start >= _Modulus1) state1start = 1; // skip impossible state1 values if (output1 != wildcard) { uint offset = NextFromStates(state0, state1start, (uint)limit); // determine how far off current output is from second output offset = offset + ((offset < output1) ? (uint)limit : 0) - (uint)output1; // compute (offset - output1) because State1 is subtracted from State0 if (offset >= 2) // skip ahead very conservatively to vicinity of a block of consecutive states that will produce 'output1' state1start += (offset - 1) * blocksize - (_Modulus - _Modulus1 + 1); // don't let skipped states cause us to miss any viable candidates while (NextFromStates(state0, state1start, (uint)limit) != output1) // TO-DO TODO: binary seek, starting at +'blocksize' state1start++; } uint state1end = (state0 + _Modulus - difflo); // State1 such that (State0 - State1) == difflo; subtract 'difflo' to get State1 upper bound if (state1end >= _Modulus) state1end -= _Modulus; if (state1end == 0 || state1end >= _Modulus1) state1end = (_Modulus1 - 1); // stop short of impossible State1 values uint state1blockstart = state1start; uint state1blockend; for (; ; ) // blocks { if (output1 != wildcard) { state1blockend = state1blockstart + blocksize; // [state1blockstart..state1blockend] is (blocksize + 1) states, to accommodate rounding variations if (state1blockend >= _Modulus) state1blockend -= _Modulus; if (state1blockend == 0 || state1blockend >= _Modulus1) state1blockend = (_Modulus1 - 1); // stop short of impossible State1 values } else { state1blockstart = state1start; state1blockend = state1end; } for (; ; ) // pieces { // brute-force State1 in two passes if its range wraps around, so that we don't have to check for zero or _Modulus1 after each increment uint state1piecestart = state1blockstart; uint state1pieceend = ((state1blockstart < state1blockend) ? state1blockend : (_Modulus1 - 1)); for (uint state1 = state1piecestart; ; state1++) { this._State0 = state0; // faster than converting states to seed and seed to states via Seed() this._State1 = state1; int i; for (i = 0; i < suboutput.Length; i++) { if (this.Next(limit) != suboutput[i] && suboutput[i] != wildcard) break; } if (i == suboutput.Length) // invoke callback for seed discovery notification { uint prevstate0 = (uint)(((ulong)state0 * _Multiplier0Inverse) % _Modulus); uint prevstate1 = (uint)(((ulong)state1 * _Multiplier1Inverse) % _Modulus1); args.EventType = RecoverSeedEventType.SeedDiscovered; args.Seed = SeedFromStates(prevstate0, prevstate1); // TO-DO TODO: estimate progress/total somehow? would be easy if not for skipped State1 candidates args.CurrentAttempts = 0; args.TotalAttempts = 0; callback(args); if (args.Cancel) return false; } if (state1 == state1pieceend) // check only after body of loop so that 'state1pieceend' will get tested break; } //for(state1) if (state1blockstart > state1blockend) state1blockstart = 1; // now brute-force the second (post-zero) piece else break; } //for(pieces) if (output1 != wildcard) { if (state1blockstart <= state1end && state1blockend >= state1end) break; state1blockstart = state1blockend + ((uint)(limit - 1) * blocksize); // skip ahead by a conservative underestimate if (state1blockend <= state1end && state1blockstart >= state1end) break; // 'state1blockend' will be set at the top of the loop body while (NextFromStates(state0, state1blockstart, (uint)limit) != output1) // TO-DO TODO: binary seek? state1blockstart++; } else break; } //for(blocks) } //for(state0) } //unchecked return true; }
protected override bool RecoverSeed(ulong seedStart, ulong seedEnd, ulong seedIncrement, ulong[] output, ulong limit, ulong wildcard, RecoverSeedCallback callback, ulong progressInterval) { if (output.Length < 2 || output[0] > (ulong.MaxValue / _OutputDivisor) || output[0] == wildcard || limit == 0 || limit > _OutputDivisor) { return(base.RecoverSeed(seedStart, seedEnd, seedIncrement, output, limit, wildcard, callback, progressInterval)); } if (seedStart > seedEnd || seedIncrement == 0) { return(false); } if (unchecked (limit & (limit - 1)) == 0) // special handling for power-of-two limits { return(this.RecoverSeedPowerOf2(seedStart, seedEnd, seedIncrement, output, limit, wildcard, callback, progressInterval)); } RecoverSeedEventArgs args = new RecoverSeedEventArgs(); unchecked // disable arithmetic checks for performance { // operate on output suffix of length N - 1 so we can fix middle portion of seed to output[0] ulong[] suboutput = new ulong[output.Length - 1]; Array.Copy(output, 1, suboutput, 0, output.Length - 1); ulong seedtopincr = (uint)limit * _DiscardDivisorForMod; // Modulus is a power of 2, so GCD(limit, Modulus) is greatest power-of-two factor of limit ulong subgenoutputmask = ((uint)limit ^ ((uint)limit - 1)) >> 1; // fix bottom portion by brute-forcing subgenerator ulong subseed, subseedend; for (subseed = (output[0] & subgenoutputmask) * _DiscardDivisorForMod, subseedend = subseed + _DiscardDivisorForMod - 1; subseed != subseedend; subseed++) { if (subgenoutputmask != 0) // if limit is odd, we can't check this subgenerator seed against LSB(s) of output; we must test all possibilities below { this._LcgState = subseed; // don't use Seed() because it XORs by _Multiplier int i; for (i = 0; i < suboutput.Length; i++) { if ((this.Next(limit) & subgenoutputmask) != (suboutput[i] & subgenoutputmask) && suboutput[i] != wildcard) { break; } } if (i < suboutput.Length) { continue; } } // MSB(s) of subseed will contain LSB(s) of output[0], so | instead of + to keep duplicate bit(s) from interfering for (ulong seed = (output[0] * _DiscardDivisorForMod) | subseed; seed <= seedEnd; seed += seedtopincr) { this._LcgState = seed; // don't use Seed() because it XORs by _Multiplier int i; for (i = 0; i < suboutput.Length; i++) { if (this.Next(limit) != suboutput[i] && suboutput[i] != wildcard) { break; } } if (i == suboutput.Length) // invoke callback for seed discovery notification { args.EventType = RecoverSeedEventType.SeedDiscovered; args.Seed = (PreviousState(seed) ^ _Multiplier); // TO-DO TODO: estimate progress/total somehow? args.CurrentAttempts = 0; args.TotalAttempts = 0; callback(args); if (args.Cancel) { return(false); } } } //for(seed<=seedEnd) } //for(subseed<subgensize) } //unchecked return(true); } //PrngJava.RecoverSeed
/// <summary> /// Discovers the seed or seeds that produce the supplied PRNG output sequence. /// </summary> /// <param name="seedStart">The first seed value to test.</param> /// <param name="seedEnd">The last seed value that could be tested.</param> /// <param name="seedIncrement">A positive integer specifying the interval at which to test seeds. /// A value of 1 tests each seed serially, a value of 2 tests every other seed, and so on.</param> /// <param name="output">A sequence containing one or more PRNG output values (modulo <paramref name="limit"/>) /// and any number of instances of <paramref name="wildcard"/>.</param> /// <param name="limit">The lowest positive integer greater than the maximum allowed output value, or 0 if no limit was imposed on the output.</param> /// <param name="wildcard">A number which will match any PRNG output value when encountered in <paramref name="output"/>.</param> /// <param name="callback">A method to invoke with seed discovery or progress notifications.</param> /// <param name="progressInterval">The number of seeds to test between progress notifications, or 0 to not receive progress notifications.</param> /// <returns>true if seed discovery completed (even if no seed was discovered), or false if the callback canceled discovery or an error occurred.</returns> /// <remarks>If progress notifications are requested, <paramref name="callback"/> will be invoked for a final progress notification event before true is returned.</remarks> protected virtual bool RecoverSeed(ulong seedStart, ulong seedEnd, ulong seedIncrement, ulong[] output, ulong limit, ulong wildcard, RecoverSeedCallback callback, ulong progressInterval) { if (seedStart > seedEnd || seedIncrement == 0) { return(false); } if (callback == null) { progressInterval = 0; } ulong totalcount = unchecked ((seedEnd - seedStart) / seedIncrement + 1); // count = 0 (2**64) if minimum seed = 0, maxseed = 2**64 - 1, and seedIncrement = 1 ulong totalcountdown = totalcount; RecoverSeedEventArgs args = new RecoverSeedEventArgs(); ulong seed = seedStart; do { ulong countdown = ((totalcountdown < progressInterval || progressInterval == 0) ? totalcountdown : progressInterval); totalcountdown = unchecked (totalcountdown - countdown); // allow integer underflow so that 0 == 2**64 do { this.Seed(seed); int i; if (limit != 0) { for (i = 0; i < output.Length; i++) { if (this.Next(limit) != output[i] && output[i] != wildcard) { break; } } } else { for (i = 0; i < output.Length; i++) { if (this.Next() != output[i] && output[i] != wildcard) { break; } } } if (i == output.Length) // invoke callback for seed discovery notification { args.EventType = RecoverSeedEventType.SeedDiscovered; args.Seed = seed; args.CurrentAttempts = (totalcount - totalcountdown - countdown); args.TotalAttempts = totalcount; callback(args); if (args.Cancel) { return(false); } } seed += seedIncrement; }while (unchecked (--countdown) != 0); // allow integer underflow here, so that 0 is equivalent to 2**64 if (progressInterval != 0) // invoke callback for progress notification { args.EventType = RecoverSeedEventType.ProgressNotification; args.Seed = seed; args.CurrentAttempts = (totalcount - totalcountdown - countdown); args.TotalAttempts = totalcount; callback(args); if (args.Cancel) { return(false); } } }while (totalcountdown != 0); return(true); } //PrngBase.RecoverSeed(ulong,ulong,ulong,ulong[],ulong,ulong,RecoverSeedCallback,ulong)
protected override bool RecoverSeed(ulong seedStart, ulong seedEnd, ulong seedIncrement, ulong[] output, ulong limit, ulong wildcard, RecoverSeedCallback callback, ulong progressInterval) { /// TO-DO TODO: make this more accurate in general, account for off-by-one (esp. in output[0] position), etc. if (output.Length < 2 || output[0] > uint.MaxValue || output[0] == wildcard || limit == 0 || limit > (ulong)uint.MaxValue + 1 || seedIncrement != 1) { return(base.RecoverSeed(seedStart, seedEnd, seedIncrement, output, limit, wildcard, callback, progressInterval)); } if (seedStart > seedEnd) { return(false); } if (callback == null) { progressInterval = 0; } ulong output1 = ((output.Length > 2) ? output[1] : wildcard); uint blocksize = (uint)(0x100000000 / ((ulong)0x10000 * limit)); RecoverSeedEventArgs args = new RecoverSeedEventArgs(); unchecked // disable arithmetic checks for performance { // operate on output suffix of length N - 1 so we can fix middle portion of seed to output[0] ulong[] suboutput = new ulong[output.Length - 1]; Array.Copy(output, 1, suboutput, 0, output.Length - 1); // lowstart..lowend define the range of values that the low 18 bits of State0 could have taken after producing the observed output[0] uint lowstart = (uint)((output[0] * 0x100000000 + (limit - 1)) / limit) >> 14; uint lowend = (uint)((((output[0] + 1) * 0x100000000 + (limit - 1)) / limit) - 1) >> 14; ulong totalcount = (ulong)(lowend - lowstart) * (((ulong)_Multiplier0 / 4) + 1); ulong totalcountdown = totalcount; ulong countdown = ((totalcountdown < progressInterval || progressInterval == 0) ? totalcountdown : progressInterval); int skip = (output1 == wildcard) ? -1 : 0; if (blocksize < 2) { skip = -1; // don't attempt improved attack if block size is too small } for (uint statelow = lowstart; statelow < lowend; statelow++) { ulong rangestart = ((ulong)statelow << 32) | 1; ulong rangeend = rangestart + (((ulong)_Multiplier0 / 4) << 50); for (ulong seed = rangestart; seed <= rangeend;) { uint rounds = (uint)(((rangeend - seed) >> 50) + 1); if (rounds > countdown) { rounds = (uint)countdown; } uint roundnum = 0; if (skip > 0) { if ((uint)skip >= rounds) { seed += 0x4000000000000 * (ulong)rounds; skip -= (int)(rounds - roundnum); roundnum = rounds; } else { roundnum = (uint)skip; seed += 0x4000000000000 * (ulong)(uint)skip; skip = 0; } } // we have the low 18 bits of State0 confined to a range, but we still need to brute-force the top 14 bits (up to Multiplier0 / 4) for (; roundnum < rounds; seed += 0x4000000000000, roundnum++) { this.Seed(seed); uint o1test = (uint)this.Next(limit); if (o1test != output1 && output1 != wildcard) { if (skip == 0) { uint diff = ((uint)output1 + (uint)limit - o1test) % (uint)limit; if (diff > 1) { skip = (int)((diff - 1) * blocksize); if ((uint)skip >= (rounds - roundnum)) { seed += 0x4000000000000 * (ulong)(rounds - roundnum); skip -= (int)(rounds - roundnum); roundnum = rounds; break; } roundnum += (uint)(skip - 1); seed += 0x4000000000000 * (ulong)(uint)(skip - 1); skip = 0; } } continue; } else if (skip > 0) { skip = 0; } int i; for (i = 1; i < suboutput.Length; i++) { if (this.Next(limit) != suboutput[i] && suboutput[i] != wildcard) { break; } } if (i >= suboutput.Length) // invoke callback for seed discovery notification { args.EventType = RecoverSeedEventType.SeedDiscovered; args.Seed = unchecked (((ulong)PreviousState0((uint)(seed >> 32)) << 32) | PreviousState1((uint)seed)); args.CurrentAttempts = (totalcount - totalcountdown + roundnum); args.TotalAttempts = totalcount; callback(args); if (args.Cancel) { return(false); } } } //for(roundnum<rounds) totalcountdown -= rounds; countdown -= rounds; if (countdown == 0 && progressInterval != 0) { args.EventType = RecoverSeedEventType.ProgressNotification; args.Seed = seed; args.CurrentAttempts = (totalcount - totalcountdown); args.TotalAttempts = totalcount; callback(args); if (args.Cancel) { return(false); } countdown = ((totalcountdown < progressInterval) ? totalcountdown : progressInterval); } } //for(seed<=rangeend) } //for(statelow<lowend) } //unchecked return(true); } //PrngV8.RecoverSeed
protected override bool RecoverSeed(ulong seedStart, ulong seedEnd, ulong seedIncrement, ulong[] output, ulong limit, ulong wildcard, RecoverSeedCallback callback, ulong progressInterval) { if (output.Length < 2 || output[0] > (ulong.MaxValue / _OutputDivisor) || output[0] == wildcard || limit == 0 || limit > _OutputDivisor) { return(base.RecoverSeed(seedStart, seedEnd, seedIncrement, output, limit, wildcard, callback, progressInterval)); } if (seedStart > seedEnd || seedIncrement == 0) { return(false); } unchecked // disable arithmetic checks for performance { // operate on output suffix of length N - 1 so we can fix top portion of seed to output[0] ulong[] suboutput = new ulong[output.Length - 1]; Array.Copy(output, 1, suboutput, 0, output.Length - 1); ulong rangestart = ((_OutputDivisor * output[0]) + (uint)limit - 1) / (uint)limit; // add (limit - 1) to round up, to make sure 'rangestart' will produce output[0] rangestart *= _DiscardDivisor; ulong rangeend = (((_OutputDivisor * (output[0] + 1)) + (uint)limit - 1) / (uint)limit) - 1; // lowest seed that produces (output[0] + 1), then - 1 rangeend = (rangeend * _DiscardDivisor) + (_DiscardDivisor - 1); if (rangestart <= seedStart) { rangestart = seedStart; } else if (seedIncrement > 1) { ulong incroffset = (rangestart - seedStart) % seedIncrement; if (incroffset != 0) { rangestart += (seedIncrement - incroffset); // advance to the next multiple of 'seedIncrement' (relative to 'seedStart') } } if (rangeend > seedEnd) { rangeend = seedEnd; } ulong output1 = ((output.Length > 2) ? output[1] : wildcard); uint blocksize = (uint)(_Modulus / ((ulong)_Multiplier * limit)); if (blocksize < 2) { output1 = wildcard; // don't attempt improved attack if block size is too small } if (output1 == wildcard || seedIncrement != 1) { this._OriginalCallback = callback; return(base.RecoverSeed(rangestart, rangeend, seedIncrement, suboutput, limit, wildcard, this.RecoverSeedCallback, progressInterval)); } // otherwise, use second output to skip blocks of candidate states RecoverSeedEventArgs args = new RecoverSeedEventArgs(); uint offset = NextFromState((uint)rangestart, (uint)limit); // determine how far off current output is from second output offset = (uint)output1 + ((output1 < offset) ? (uint)limit : 0) - offset; if (offset >= 2) // skip ahead conservatively to vicinity of a block of consecutive states that will produce 'output1' { rangestart += (offset - 1) * blocksize; } while (NextFromState((uint)rangestart, (uint)limit) != output1) // TO-DO TODO: binary seek, starting at +'blocksize' { rangestart++; } uint blockstart = (uint)rangestart; uint blockend; for (; ;) { blockend = blockstart + blocksize; // [blockstart..blockend] is (blocksize + 1) states, to accommodate rounding variations if (blockend >= rangeend) { blockend = (uint)rangeend; } for (uint state = blockstart; ; state++) { this.Seed(state); int i; for (i = 0; i < suboutput.Length; i++) { if (this.Next(limit) != suboutput[i] && suboutput[i] != wildcard) { break; } } if (i == suboutput.Length) // invoke callback for seed discovery notification { args.EventType = RecoverSeedEventType.SeedDiscovered; args.Seed = PreviousState(state); // TO-DO TODO: estimate progress/total somehow? args.CurrentAttempts = 0; args.TotalAttempts = 0; callback(args); if (args.Cancel) { return(false); } } if (state == blockend) // check only after body of loop so that 'blockend' will get tested { break; } } //for(state) blockstart += ((uint)limit * blocksize); if (blockstart >= rangeend) { break; } // 'blockend' will be set at the top of the loop body while (NextFromState(blockstart, (uint)limit) != output1) // TO-DO TODO: binary seek? { blockstart++; } } //for(;;) } //unchecked return(true); } //PrngMsvcrtMul.RecoverSeed
protected override bool RecoverSeed(ulong seedStart, ulong seedEnd, ulong seedIncrement, ulong[] output, ulong limit, ulong wildcard, RecoverSeedCallback callback, ulong progressInterval) { /// TO-DO TODO: make this more accurate in general, account for off-by-one (esp. in output[0] position), etc. if (output.Length < 2 || output[0] > uint.MaxValue || output[0] == wildcard || limit == 0 || limit > (ulong)uint.MaxValue + 1 || seedIncrement != 1) return base.RecoverSeed(seedStart, seedEnd, seedIncrement, output, limit, wildcard, callback, progressInterval); if (seedStart > seedEnd) return false; if (callback == null) progressInterval = 0; ulong output1 = ((output.Length > 2) ? output[1] : wildcard); uint blocksize = (uint)(0x100000000 / ((ulong)0x10000 * limit)); RecoverSeedEventArgs args = new RecoverSeedEventArgs(); unchecked // disable arithmetic checks for performance { // operate on output suffix of length N - 1 so we can fix middle portion of seed to output[0] ulong[] suboutput = new ulong[output.Length - 1]; Array.Copy(output, 1, suboutput, 0, output.Length - 1); // lowstart..lowend define the range of values that the low 18 bits of State0 could have taken after producing the observed output[0] uint lowstart = (uint)((output[0] * 0x100000000 + (limit - 1)) / limit) >> 14; uint lowend = (uint)((((output[0] + 1) * 0x100000000 + (limit - 1)) / limit) - 1) >> 14; ulong totalcount = (ulong)(lowend - lowstart) * (((ulong)_Multiplier0 / 4) + 1); ulong totalcountdown = totalcount; ulong countdown = ((totalcountdown < progressInterval || progressInterval == 0) ? totalcountdown : progressInterval); int skip = (output1 == wildcard) ? -1 : 0; if (blocksize < 2) skip = -1; // don't attempt improved attack if block size is too small for (uint statelow = lowstart; statelow < lowend; statelow++) { ulong rangestart = ((ulong)statelow << 32) | 1; ulong rangeend = rangestart + (((ulong)_Multiplier0 / 4) << 50); for (ulong seed = rangestart; seed <= rangeend; ) { uint rounds = (uint)(((rangeend - seed) >> 50) + 1); if (rounds > countdown) rounds = (uint)countdown; uint roundnum = 0; if (skip > 0) { if ((uint)skip >= rounds) { seed += 0x4000000000000 * (ulong)rounds; skip -= (int)(rounds - roundnum); roundnum = rounds; } else { roundnum = (uint)skip; seed += 0x4000000000000 * (ulong)(uint)skip; skip = 0; } } // we have the low 18 bits of State0 confined to a range, but we still need to brute-force the top 14 bits (up to Multiplier0 / 4) for (; roundnum < rounds; seed += 0x4000000000000, roundnum++) { this.Seed(seed); uint o1test = (uint)this.Next(limit); if (o1test != output1 && output1 != wildcard) { if (skip == 0) { uint diff = ((uint)output1 + (uint)limit - o1test) % (uint)limit; if (diff > 1) { skip = (int)((diff - 1) * blocksize); if ((uint)skip >= (rounds - roundnum)) { seed += 0x4000000000000 * (ulong)(rounds - roundnum); skip -= (int)(rounds - roundnum); roundnum = rounds; break; } roundnum += (uint)(skip - 1); seed += 0x4000000000000 * (ulong)(uint)(skip - 1); skip = 0; } } continue; } else if (skip > 0) skip = 0; int i; for (i = 1; i < suboutput.Length; i++) { if (this.Next(limit) != suboutput[i] && suboutput[i] != wildcard) break; } if (i >= suboutput.Length) // invoke callback for seed discovery notification { args.EventType = RecoverSeedEventType.SeedDiscovered; args.Seed = unchecked(((ulong)PreviousState0((uint)(seed >> 32)) << 32) | PreviousState1((uint)seed)); args.CurrentAttempts = (totalcount - totalcountdown + roundnum); args.TotalAttempts = totalcount; callback(args); if (args.Cancel) return false; } } //for(roundnum<rounds) totalcountdown -= rounds; countdown -= rounds; if (countdown == 0 && progressInterval != 0) { args.EventType = RecoverSeedEventType.ProgressNotification; args.Seed = seed; args.CurrentAttempts = (totalcount - totalcountdown); args.TotalAttempts = totalcount; callback(args); if (args.Cancel) return false; countdown = ((totalcountdown < progressInterval) ? totalcountdown : progressInterval); } } //for(seed<=rangeend) } //for(statelow<lowend) } //unchecked return true; }
protected override bool RecoverSeed(ulong seedStart, ulong seedEnd, ulong seedIncrement, ulong[] output, ulong limit, ulong wildcard, RecoverSeedCallback callback, ulong progressInterval) { if (output.Length < 2 || output[0] >= limit || output[0] == wildcard || limit == 0 || limit > _Modulus || seedIncrement != 1) { return(base.RecoverSeed(seedStart, seedEnd, seedIncrement, output, limit, wildcard, callback, progressInterval)); } if (seedStart < this.MinimumSeed) { seedStart = this.MinimumSeed; } if (seedEnd > this.MaximumSeed) { seedEnd = this.MaximumSeed; } if (seedStart > seedEnd) { return(false); } ulong output1 = ((output.Length > 2) ? output[1] : wildcard); uint blocksize = (uint)(_Modulus / ((ulong)_Multiplier1 * limit)); if (blocksize < 2) { output1 = wildcard; // don't attempt improved attack if block size is too small } RecoverSeedEventArgs args = new RecoverSeedEventArgs(); unchecked // disable arithmetic checks for performance { // operate on output suffix of length N - 1 so we can fix bottom portion of seed to output[0] ulong[] suboutput = new ulong[output.Length - 1]; Array.Copy(output, 1, suboutput, 0, output.Length - 1); // 2147483589.46728 rounded down is 2147483589 and rounded up is 2147483590; rounding outward ensures we won't exclude a viable candidate state uint difflo = (uint)(((2147483589 * output[0]) + limit - 1) / limit); // add (limit - 1) to round up, to make sure 'difflo' will (almost always) produce output[0] uint diffhi = (uint)(((2147483590 * (output[0] + 1)) + limit - 1) / limit) - 1; // lowest difference that produces (output[0] + 1), then - 1 for (uint state0 = 1; state0 < _Modulus; state0++) { uint state1start = (state0 + _Modulus - diffhi); // State1 such that (State0 - State1) == diffhi; subtract 'diffhi' to get State1 lower bound if (state1start >= _Modulus) // _Modulus rather than _Modulus1 because difference is computed modulo _Modulus { state1start -= _Modulus; } if (state1start == 0 || state1start >= _Modulus1) { state1start = 1; // skip impossible state1 values } if (output1 != wildcard) { uint offset = NextFromStates(state0, state1start, (uint)limit); // determine how far off current output is from second output offset = offset + ((offset < output1) ? (uint)limit : 0) - (uint)output1; // compute (offset - output1) because State1 is subtracted from State0 if (offset >= 2) // skip ahead very conservatively to vicinity of a block of consecutive states that will produce 'output1' { state1start += (offset - 1) * blocksize - (_Modulus - _Modulus1 + 1); // don't let skipped states cause us to miss any viable candidates } while (NextFromStates(state0, state1start, (uint)limit) != output1) // TO-DO TODO: binary seek, starting at +'blocksize' { state1start++; } } uint state1end = (state0 + _Modulus - difflo); // State1 such that (State0 - State1) == difflo; subtract 'difflo' to get State1 upper bound if (state1end >= _Modulus) { state1end -= _Modulus; } if (state1end == 0 || state1end >= _Modulus1) { state1end = (_Modulus1 - 1); // stop short of impossible State1 values } uint state1blockstart = state1start; uint state1blockend; for (; ;) // blocks { if (output1 != wildcard) { state1blockend = state1blockstart + blocksize; // [state1blockstart..state1blockend] is (blocksize + 1) states, to accommodate rounding variations if (state1blockend >= _Modulus) { state1blockend -= _Modulus; } if (state1blockend == 0 || state1blockend >= _Modulus1) { state1blockend = (_Modulus1 - 1); // stop short of impossible State1 values } } else { state1blockstart = state1start; state1blockend = state1end; } for (; ;) // pieces { // brute-force State1 in two passes if its range wraps around, so that we don't have to check for zero or _Modulus1 after each increment uint state1piecestart = state1blockstart; uint state1pieceend = ((state1blockstart < state1blockend) ? state1blockend : (_Modulus1 - 1)); for (uint state1 = state1piecestart; ; state1++) { this._State0 = state0; // faster than converting states to seed and seed to states via Seed() this._State1 = state1; int i; for (i = 0; i < suboutput.Length; i++) { if (this.Next(limit) != suboutput[i] && suboutput[i] != wildcard) { break; } } if (i == suboutput.Length) // invoke callback for seed discovery notification { uint prevstate0 = (uint)(((ulong)state0 * _Multiplier0Inverse) % _Modulus); uint prevstate1 = (uint)(((ulong)state1 * _Multiplier1Inverse) % _Modulus1); args.EventType = RecoverSeedEventType.SeedDiscovered; args.Seed = SeedFromStates(prevstate0, prevstate1); // TO-DO TODO: estimate progress/total somehow? would be easy if not for skipped State1 candidates args.CurrentAttempts = 0; args.TotalAttempts = 0; callback(args); if (args.Cancel) { return(false); } } if (state1 == state1pieceend) // check only after body of loop so that 'state1pieceend' will get tested { break; } } //for(state1) if (state1blockstart > state1blockend) { state1blockstart = 1; // now brute-force the second (post-zero) piece } else { break; } } //for(pieces) if (output1 != wildcard) { if (state1blockstart <= state1end && state1blockend >= state1end) { break; } state1blockstart = state1blockend + ((uint)(limit - 1) * blocksize); // skip ahead by a conservative underestimate if (state1blockend <= state1end && state1blockstart >= state1end) { break; } // 'state1blockend' will be set at the top of the loop body while (NextFromStates(state0, state1blockstart, (uint)limit) != output1) // TO-DO TODO: binary seek? { state1blockstart++; } } else { break; } } //for(blocks) } //for(state0) } //unchecked return(true); } //PrngMssql.RecoverSeed
protected override bool RecoverSeed(ulong seedStart, ulong seedEnd, ulong seedIncrement, ulong[] output, ulong limit, ulong wildcard, RecoverSeedCallback callback, ulong progressInterval) { if (output.Length < 2 || output[0] >= _OutputDivisor || output[0] == wildcard || seedStart != this.MinimumSeed || seedIncrement != 1 || limit == 0 || limit > _OutputDivisor) { return(base.RecoverSeed(seedStart, seedEnd, seedIncrement, output, limit, wildcard, callback, progressInterval)); } if (seedStart > seedEnd || seedIncrement == 0) { return(false); } RecoverSeedEventArgs args = new RecoverSeedEventArgs(); unchecked // disable arithmetic checks for performance { // operate on output suffix of length N - 1 so we can fix middle portion of seed to output[0] ulong[] suboutput = new ulong[output.Length - 1]; Array.Copy(output, 1, suboutput, 0, output.Length - 1); uint seedtopincr = (uint)limit * _DiscardDivisor; // Modulus is a power of 2, so GCD(limit, Modulus) is greatest power-of-two factor of limit uint subgenoutputmask = ((uint)limit ^ ((uint)limit - 1)) >> 1; uint subgensize = (subgenoutputmask + 1) * _DiscardDivisor; // GCD(limit, Modulus) * DiscardDivisor // fix bottom portion by brute-forcing subgenerator uint subseed; for (subseed = 0; subseed < subgensize; subseed++) { if (subgenoutputmask != 0) // if limit is odd, we can't check this subgenerator seed against LSB(s) of output; we must test all possibilities below { this.Seed(subseed); int i; for (i = 0; i < suboutput.Length; i++) { // we can do Next() instead of Next(limit) for speed, since either way the remainder modulo power-of-two is preserved if ((this.Next(limit) & subgenoutputmask) != (suboutput[i] & subgenoutputmask) && suboutput[i] != wildcard) { break; } } if (i < suboutput.Length) { continue; } } // MSB(s) of subseed will contain LSB(s) of output[0], so | instead of + to keep duplicate bit(s) from interfering for (uint seed = ((uint)output[0] * _DiscardDivisor) | subseed; seed <= seedEnd; seed += seedtopincr) { this.Seed(seed); int i; for (i = 0; i < suboutput.Length; i++) { if (this.Next(limit) != suboutput[i] && suboutput[i] != wildcard) { break; } } if (i == suboutput.Length) // invoke callback for seed discovery notification { args.EventType = RecoverSeedEventType.SeedDiscovered; args.Seed = PreviousState(seed); // TO-DO TODO: estimate progress/total somehow? args.CurrentAttempts = 0; args.TotalAttempts = 0; callback(args); if (args.Cancel) { return(false); } } } //for(seed<=seedEnd) } //for(subseed<subgensize) } //unchecked return(true); } //PrngMsvcrt.RecoverSeed
protected override bool RecoverSeed(ulong seedStart, ulong seedEnd, ulong seedIncrement, ulong[] output, ulong limit, ulong wildcard, RecoverSeedCallback callback, ulong progressInterval) { if (output.Length < 2 || output[0] >= _OutputDivisor || output[0] == wildcard || seedStart != this.MinimumSeed || seedIncrement != 1 || limit == 0 || limit > _OutputDivisor) return base.RecoverSeed(seedStart, seedEnd, seedIncrement, output, limit, wildcard, callback, progressInterval); if (seedStart > seedEnd || seedIncrement == 0) return false; RecoverSeedEventArgs args = new RecoverSeedEventArgs(); unchecked // disable arithmetic checks for performance { // operate on output suffix of length N - 1 so we can fix middle portion of seed to output[0] ulong[] suboutput = new ulong[output.Length - 1]; Array.Copy(output, 1, suboutput, 0, output.Length - 1); uint seedtopincr = (uint)limit * _DiscardDivisor; // Modulus is a power of 2, so GCD(limit, Modulus) is greatest power-of-two factor of limit uint subgenoutputmask = ((uint)limit ^ ((uint)limit - 1)) >> 1; uint subgensize = (subgenoutputmask + 1) * _DiscardDivisor; // GCD(limit, Modulus) * DiscardDivisor // fix bottom portion by brute-forcing subgenerator uint subseed; for (subseed = 0; subseed < subgensize; subseed++) { if (subgenoutputmask != 0) // if limit is odd, we can't check this subgenerator seed against LSB(s) of output; we must test all possibilities below { this.Seed(subseed); int i; for (i = 0; i < suboutput.Length; i++) { // we can do Next() instead of Next(limit) for speed, since either way the remainder modulo power-of-two is preserved if ((this.Next(limit) & subgenoutputmask) != (suboutput[i] & subgenoutputmask) && suboutput[i] != wildcard) break; } if (i < suboutput.Length) continue; } // MSB(s) of subseed will contain LSB(s) of output[0], so | instead of + to keep duplicate bit(s) from interfering for (uint seed = ((uint)output[0] * _DiscardDivisor) | subseed; seed <= seedEnd; seed += seedtopincr) { this.Seed(seed); int i; for (i = 0; i < suboutput.Length; i++) { if (this.Next(limit) != suboutput[i] && suboutput[i] != wildcard) break; } if (i == suboutput.Length) // invoke callback for seed discovery notification { args.EventType = RecoverSeedEventType.SeedDiscovered; args.Seed = PreviousState(seed); // TO-DO TODO: estimate progress/total somehow? args.CurrentAttempts = 0; args.TotalAttempts = 0; callback(args); if (args.Cancel) return false; } } //for(seed<=seedEnd) } //for(subseed<subgensize) } //unchecked return true; }