0

I'm looking for easy to remember names in a terabyte of generated .onion addresses.

In essence I need the opposite of a password generator, that scans for a easy to remember pattern that I can use for a HSP.

Desired traits include:

  • US Dictionary match
  • Repeating characters
  • Sequential letters/numbers
  • Patterns
  • ???

How should I approach my search of pre-generated keys in XML form?

3
  • 2
    This might be better asked in a general programming SE forum? The association to Tor seems incidental. Parsing XML and matching for specific attributes of strings in XML elements.
    – cacahuatl
    Jul 25 '16 at 21:28
  • @canonizingironize It does have a use case specific to ToR, and I will write it myself if you can add/modify the traits Jul 26 '16 at 12:45
  • That's like saying: "I want to send a message by post that states the true nature of the P=NP conjecture. How do I solve P=NP?" and deciding that the people to ask about solving P=NP is the postoffice and explaining it is up to them to answer it because you intend to mail the result. It's not related to Tor, except tangentially.
    – cacahuatl
    Jul 26 '16 at 18:09
1

Alright, I'll throw my hat in the ring.

This is an act of purely passive-aggressive coding.

First, we read in words from dictionaries (only using words that contain valid onion values), then we use those words to construct a tree. Each "branch" is a comparison, so if the word starts with 'h', we create an 'h' branch from the root, then and 'e' branch from 'h', and an 'l' branch from 'e', etc. At the end of each word we leave a boolean flag to say that it denotes where a full word ends. Each valid path through the tree represents a word in our dictionary.

for example 'hello', 'hi', 'helping', and 'help' become a structure like this, where * denotes the end of a full known word.

[h]___[e]___[l]___[l]___[o]*
    |_[i]*      |_[p]*__[i]___[n]___[g]*

Then we generate our onions, for each letter in the onion address we check if there is a valid branch in our tree. If there is, we follow it and check the next letter against the branches stemming from it. Using this method, we compare the onion address to every word in the dictionary simultaneously, so the number of comparisons to check if a valid word in the dictionary is found is, in the worst case, the length of the onion address regardless of how many words we have in our dictionary.

I've implemented this in C, and also included libssl code for generating, encoding, matching and saving matches found.

It has a bonus mode where it will match against onion addresses that consist purely of words from the dictionary.

The code is available here, dictionaries not included.

$ ./main -t 7 -d ./words
Populating search from './words'
Starting thread 1/7...Done
[...SNIP...]
Starting thread 7/7...Done
Starting search. Use ctrl-c to exit.
main: found 'wannakgcqdivzm4p'
main: found 'culchndmw4lufstt'
main: found 'butte3ittosuqf7y'
main: found 'fading25mmobu3rc'
main: found 'flake7cjdl2e4wg6'
^CCleaning up...
Stopping thread 1/7...Done
[...SNIP...]
Stopping thread 7/7...Done
Bye o/
$ cat fading25mmobu3rc
-----BEGIN RSA PRIVATE KEY-----
MII[...SNIP...]
-----END RSA PRIVATE KEY-----
0

Part 1: Get all the Onion addresses: (powershell)

$files = get-childitem c:\dev -filter "*.txt"

foreach ($file in $files)
{
    $data =  Select-String -Pattern "<Hash>" -Path c:\dev\$file

   foreach ($lineMatch in $data)
   {
        $lineSource = $lineMatch.ToString()
        $lineSource2 =  $lineSource.Replace(".onion</Hash>","")
        $lineSource3 = $lineSource2.Replace("<Hash>","")

   $lineSource3 | add-content "found.dat"

   } 

}

Part2: Iterate through the dictionary against each string. Performance boost by doing this in parallel.

// Quick hack - pardon the poor coding practices.  
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace ConsoleApplication2
{
    class Program
    {
        static object lockObject = new object();
        static System.Collections.Concurrent.ConcurrentDictionary<string, List<string>> WordsDict = new System.Collections.Concurrent.ConcurrentDictionary<string, List<string>>();

        static void Main(string[] args)
        {

            var count = 0;

            using (StreamReader sr = File.OpenText("c:\\words.txt"))
            {
                string s = String.Empty;
                while ((s = sr.ReadLine()) != null)
                {
                    count++;
                }
            }

            //the dictionary chars/strings to look for. We use both because we're testing some string methods too.
            string[] sf = new string[count];

            int line = 0;
            using (StreamReader sr = File.OpenText("c:\\words.txt"))
            {
                string s = String.Empty;
                while ((s = sr.ReadLine()) != null)
                {
                    sf[line] = s;
                    line++;
                }
            }


            count = 0;
            using (StreamReader sr = File.OpenText("c:\\agent77\\scallionOutput.txt"))
            {
                string s = String.Empty;
                while ((s = sr.ReadLine()) != null)
                {
                    count++;
                }
            }
            string[] ss = new string[count];

            line = 0;
            using (StreamReader sr = File.OpenText("c:\\agent77\\scallionOutput.txt"))
            {
                string s = String.Empty;
                while ((s = sr.ReadLine()) != null)
                {
                    ss[line] = s;
                    line++;
                }
            }

            //the strings to search (line xount in <onion>)


            Parallel.For(0, ss.Length,
            () => 0,
            (x, loopState, subtotal) =>
            { 
                for (int y = 0; y < sf.Length; y++)
                {
                    if (sf[y].Length == 0 || sf[y].Length < 3)
                        continue;

                    var tt=  ((ss[x].Length - ss[x].Replace(sf[y], String.Empty).Length) / sf[y].Length > 0 ? 1 : 0);
                    subtotal += tt;

                    if (tt !=0)
                    {
                        Console.WriteLine(sf[y]);

                        List<string> wordsMatch = new List<string>();
                        wordsMatch.Add(sf[y]);

                        WordsDict.AddOrUpdate(ss[x], wordsMatch, (a, b) =>
                        {
                            b.AddRange(wordsMatch);
                            return b;
                        });
                    }

                }
                return subtotal;
            },
            (s) =>
            { 

                lock (lockObject)
                {
                    //Console.WriteLine();
                }
            }
        );
        }
    }
}
2
  • You really should take this to a programming related stackexchange site. That looks like it scales horribly...there are better and more efficient ways to approach this.
    – cacahuatl
    Aug 2 '16 at 2:07
  • @canonizingironize Scales horribly? It's a parallel for loop that looks for a partial match of 3 million words in less than a second. On a virtualized Windows instance on OSX. Pretty good if you asked me. I posted it here for anyone who wants to add exception handling, or clean it up a bit. Aug 2 '16 at 4:16
0

Generally the best approach is to determine what your desired name should look like and then use a tool to generate matches.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.