Skip to content

Instantly share code, notes, and snippets.

@JimWolff
Last active February 1, 2018 09:03
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save JimWolff/4888146283d1b3bdbb07b66db4f38f35 to your computer and use it in GitHub Desktop.
Save JimWolff/4888146283d1b3bdbb07b66db4f38f35 to your computer and use it in GitHub Desktop.
This c# code is supposed to be used with Linqpad which is free to download at (https://www.linqpad.net/) you can also use the source with any IDE/compiler that speak C# (although the .Dump() command used is linqpad specific and should be switched to something like Console.WriteLine() when using something like Visual Studio Code)
// its open source so any comments on the security or anything else is welcome, provided free of charge, but use this piece of code at your own risk, i am not liable for any damages.
const bool useRealRandom = true;
Random globalRand = new Random(); // non true-random function.
//using System.Net
void Main()
{
// user configurable values
// numberOfTimesToScramble shouldn't really make things "more random" since the time seed already makes it random, but i just want to provide several "knobs" for users to turn, to make it different for each user.
const int numberOfTimesToScramble = 13; // 1 or above.
const int minChunkSize = 4; // recommended 3-7
const int maxChunkSize = 12; // recommended 7-20
const int iotaAddressLength = 81; // max: 81, higher value is more secure, but IOTA adresses can be shorter. If more people use this generator using 80 or 79 could provide more safety against analysis of the webrequests.
const int numberOfFakeCallsToMakeDuringTheSession = 23; // any random number not too small > 13
var rnd = new Random(); // non true-random function.
var scr = Scrambler(rnd, "ABCDEFGHIJKLMNOPQRSTUVWXYZ9".ToCharArray().Cast<object>(),numberOfTimesToScramble);
var scrambledChars = new string(scr.Cast<char>().ToArray());
//scrambledChars.Dump();
var ints = GetTrueRandomIntegers(rnd,minChunkSize,maxChunkSize,iotaAddressLength,numberOfFakeCallsToMakeDuringTheSession);
Debug.WriteLine(useRealRandom + "Random IotaWallet, now keep it safe:");
Debug.WriteLine(new string(ints.Select(n => scrambledChars[n]).ToArray()));
/*
To be able to find the wallet generated this way (assuming the computer its generated on isn't compromised)
You would need to know all the random numbers, do all combinations any number of requests that total 81 ints,
*/
}
public List<object> Scrambler(Random rnd, IEnumerable<object> l, int numberOfTimesToScramble)
{
var list = l.ToList();
var localCopy = new List<Object>(list);
// here we scramble the chars to ensure that if someone knows the generated integers, they can't just directly map the integers to the characters.
List<object> scrambledObjects = new List<object>();
for (int i = 0; i < numberOfTimesToScramble; i++)
{
for (int j = 0; j < list.Count; j++) //use list.count ehre because we modify local.count
{
var chosenInt = rnd.Next(0, localCopy.Count);
var pickedObject = localCopy[chosenInt];
scrambledObjects.Add(pickedObject);
localCopy.RemoveAt(chosenInt);
}
// "reset"
localCopy = new List<Object>(scrambledObjects);
scrambledObjects = new List<Object>();
}
return localCopy;
}
public List<int> GetTrueRandomIntegers(Random rnd, int minChunkSize, int maxChunkSize, int maxLength, int numberOfFakeCalls)
{
var chunks = new List<int>();
do chunks = SplitLengthIntoChunks(rnd,minChunkSize,maxChunkSize,maxLength);
while (chunks.Last() < minChunkSize); // here we make sure the last chunk cant be identified, to preserve secrecy as we can.
// urls to call
var indexedUrls = GenerateIndexedUrls(rnd,chunks,numberOfFakeCalls,minChunkSize,maxChunkSize);
// we scramble the url order to ensure its not just a job of counting every combination from first to last request that givs 81.
// this way it can be any combination of request that gives the 81 that is used for generating the wallet.
var scrambledUrls = Scrambler(rnd,new List<Object>(indexedUrls),1).Cast<IndexedUrl>().ToList();
// random.org can see ip adresse and query pattern, which is why we make fake queries.
var scrambledInts = scrambledUrls.AsParallel().WithDegreeOfParallelism(4).Select(o => new { Index = o.Index, IsFake = o.IsFake, Ints = GetIntsFromTrueRandomAPI(o.Url)}).ToList();
var unscrambledNoFakeInts = scrambledInts.Where(o=>!o.IsFake).OrderBy(o=>o.Index).SelectMany(o=>o.Ints).ToList();
// include this part and the "Combinatorics" NuGet package to see number of possible combinations this would give.
// var combinations = new Combinations<List<int>>(scrambledInts.Select(o=>o.Ints).ToList(), chunks.Count,GenerateOption.WithoutRepetition);
// $"Combinations: {combinations.Count}".Dump();
// long count = 0;
// foreach (var c in combinations)
// {
// count++;
// if (c.Sum(e => e.Count) == 81)
// {
// //c.Dump();
// //$"{count}".Dump();
// }
// }
return unscrambledNoFakeInts;
}
public class IndexedUrl
{
public int Index { get; set; }
public bool IsFake { get; set; }
public string Url { get; set; }
public IndexedUrl(int index, bool isFake, string url)
{
Index =index;
IsFake = isFake;
Url = url;
}
}
public List<IndexedUrl> GenerateIndexedUrls(Random rnd, List<int> chunks, int numberOfFakeCalls, int minChunkSize, int maxChunkSize)
{
var isFakeCall = Enumerable.Range(0, numberOfFakeCalls).Select(_ => true).ToList();
for (int i = 0; i < chunks.Count; i++)
{
isFakeCall.Insert(rnd.Next(0, isFakeCall.Count()), false);
}
//$"{chunks.Count} = {isFakeCall.Count(o=>o==false)}".Dump();
//isFakeCall.Dump();
var numberChunks = new List<List<int>>(); // we save these numbers to do later analysis on them to ensure it is hard enough to guess
var chunksTemp = chunks;
var isFakeWithOrigIndex = new List<IndexedUrl>();
var count = 0;
foreach (var isFake in isFakeCall)
{
if (isFake)
{
isFakeWithOrigIndex.Add(new IndexedUrl(count, isFake,GetUrl(rnd.Next(minChunkSize, maxChunkSize + 1))));
}
else
{
var slice = chunksTemp.First();
isFakeWithOrigIndex.Add(new IndexedUrl(count, isFake,GetUrl(slice)));
chunksTemp = chunksTemp.Skip(1).ToList();
}
count++;
// Thread.Sleep(rnd.Next(minDelay,maxDelay)); removed since a delay between requests doesnt obscure anything when users can be tracked by their ip.
}
return isFakeWithOrigIndex;
}
public List<int> SplitLengthIntoChunks(Random rnd, int minChunkSize, int maxChunkSize, int maxLength = 81)
{
var integerChunks = new List<int>();
var lengthLeft = maxLength;
while (lengthLeft > 0)
{
var chunk = rnd.Next(minChunkSize, maxChunkSize + 1);
if (chunk > lengthLeft)
chunk = lengthLeft;
lengthLeft -= chunk;
integerChunks.Add(chunk);
}
return integerChunks;
}
public string GetUrl(int numberOfInts)
{
const string randomOrgUrl = "https://www.random.org/integers/?num={0}&min=0&max=26&col=1&base=10&format=plain&rnd=new";
return string.Format(randomOrgUrl, numberOfInts);
}
public List<int> GetIntsFromTrueRandomAPI(string url)
{
// check usage quota here: https://www.random.org/quota/
using (var wc = new WebClient())
{
if (useRealRandom)
{
var result = wc.DownloadString(url).Split(new char[] { '\r', '\n' }, StringSplitOptions.RemoveEmptyEntries).Select(w => Convert.ToInt32(w.Trim())).ToList();
//$"{DateTime.Now} - {url} - {string.Join(",", result)}".Dump(); // use this to see url results
return result;
}
else
{
var num = url.IndexOf("=")+1;
var afterNum = url.IndexOf("&");
var numberUsed = url.Substring(num,(afterNum-num));
var nums = Convert.ToInt32(numberUsed);
return new List<int>(Enumerable.Range(0,nums).Select(i=>globalRand.Next(0,27)));
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment