Added eschalot-1.2.0.tar.gz

pull/2/head
Lars Boegild Thomsen 8 years ago
commit ad5089d6cf

@ -0,0 +1,81 @@
Changes from shallot to eschalot-1.2.0:
* Support both BSD and GNU makes;
* Combined (for now) the source files into one to ease code shuffling;
* Adjusted includes a bit;
* Dropped the automatic CPUs detection and some options;
* Simplified the code somewhat;
* Limited public exponent 'e' to 4 bytes long (performance gain);
* Removed some defines, added some new ones;
* Rewrote base32 encoding function to improve performance;
* Rewrote error handling and diagnostic messages handling;
* Rewrote error handling and diagnostic messages handling again because
Linux has poor support for BSD's err(3) family of functions;
* Rearranged the code to be more in line with style(9);
* Changed a bunch of stuff, experimented for a few weeks, put it on hold
for some time, lost track of all the changes...
* Imlemented three search modes: single prefix, regular expression,
and wordlist search;
* Changed the wordlist mode to be more universal - massive performance
gains compared to the first few versions. Settled on some kind of
hybrid hashed tree for the words storage;
* Wrote a word generator (worgen) to simplify making custom wordlists;
* Reworked args and options handling - using getopt now;
* Added ifdefs and local versions of strnlen and a htobe32 macro to please
Linuxes and FreeBSD;
* Added a check for memory corruption (or some other bug - not sure yet);
* Added a routine to generate the final onion name from the final
RSA key using the same procedure as the official tor project;
* Checked the source with several versions of gcc, with pcc, and
with clang static analizer on OpenBSD, DragonFlyBSD, FreeBSD, and Linux
on 32 and 64bit platforms and on little and big endian platforms,
fixed the errors and warnings found.
Changed from shallot-0.0.2 to shallot-0.0.3:
* Fixed some memory leaks.
* More efficient use of memory.
* Added '-o' option to optimize prime size for SHA-1 hashing.
Changed from shallot-0.0.1 to shallot-0.0.2:
* Corrected some things in the README.
* Removed redundant PEM generation.
* Improved SHA-1 hashing performance.
* The previous two performance enhancements give us a ~4.6x speed boost.
That's right, ~4.6 TIMES as fast.
* Added a "sanity" limit to e, so it doesn't get insanely high during
long hashing sessions. It's adjustable, too.
* Allowed the number of threads to be specified during startup.
* Added a "monitor" mode (try it out! -m)
* Added an option to automatically daemonize
* Added file output option
* Added a verbose flag (for debugging)
* Now configures for BSD-style build on OS X.
* Allows for building on unidentified systems, in a slightly crippled form
(defaults to 1 thread unless specified).
* OpenBSD is now officially supported and tested (it lacks support
for sysctlbyname(3), wtf?).
* Removed a (small) buffer overflow.
Changes from onionhash-0.0.2 to shallot-0.0.1:
* Fixed a 1KB memory leak.
* Removed irrelevant code.
* Brute force loop no longer mallocs/frees, giving a ~1% increase in
hashing speed.
* Now completely multithreaded, allowing systems with several CPUs to
take full advantage of all of them to hash!
* Now follows PKCS#1 v2.1 for satisfactory public keys
(previously on a version prior to v2.0, i.e. v1.5).
* BSD and Linux ports (required due to autodetecting core count).

@ -0,0 +1,72 @@
Copyright (c) 2013 Unperson Hiro
Permission to use, copy, modify, and distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
--------------------------------------------------------------------------
This software is based on shallot which bears the following license:
Copyright (c) 2007 `Orum <http://hangman5naigg7rr.onion/>
<irc://irc.oftc.net:6667/shallot>
Permission is hereby granted, free of charge, to any person obtaining a
copy of this software and associated documentation files (the "Software"),
to deal in the Software without restriction, including without limitation
the rights to use, copy, modify, merge, publish, distribute, sublicense,
and/or sell copies of the Software, and to permit persons to whom the
Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.
--------------------------------------------------------------------------
This software is based on onionhash-0.0.2 which bears the following license:
If you believe in the ideology of "copyright" and what some folks call
"interlectual property" you might consider reading the following license
agreement.
However, if you're - like me - a non-believer, a "copyright atheist",
someone who thinks that information belongs to mankind itself and
cannot be posessed by a person, you're free to simply ignore it :-)
--------------------------------------------------------------------------
Copyright (c) 2006 Cowboy Bebop <bebop@torlandypjxiligx.onion>
Permission is hereby granted, free of charge, to any person obtaining a
copy of this software and associated documentation files (the "Software"),
to deal in the Software without restriction, including without limitation
the rights to use, copy, modify, merge, publish, distribute, sublicense,
and/or sell copies of the Software, and to permit persons to whom the
Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
The X.Org Foundation BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF
OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

@ -0,0 +1,67 @@
# eschalot Makefile
# If you modify the Makefile, please make sure it works with
# both BSD and GNU makes. Avoid the fancy features.
PROG1 ?= eschalot
PROG2 ?= worgen
PROG3 ?= typecard
PREFIX ?= /usr/local
BINDIR ?= ${PREFIX}/bin
LIBS += -lpthread -lssl -lcrypto
WARNINGS += -Wall -W -Wunused -pedantic -Wpointer-arith \
-Wreturn-type -Wstrict-prototypes \
-Wmissing-prototypes -Wshadow -Wcast-qual -Wextra
CFLAGS += -std=c99
CFLAGS += -O2
CFLAGS += -fPIC
CFLAGS += -finline-functions
#CFLAGS += -fomit-frame-pointer
#CFLAGS += -m64
#CFLAGS += -mtune=native -march=native
#CFLAGS += -g
CC ?= cc
INSTALL ?= install -c -o root -g bin -m 755
RM ?= /bin/rm -f
all: ${PROG1} ${PROG2}
${PROG1}: ${PROG1}.c Makefile
${CC} ${CFLAGS} ${WARNINGS} -o $@ ${PROG1}.c ${LIBS}
${PROG2}: ${PROG2}.c Makefile
${CC} ${CFLAGS} ${WARNINGS} -o $@ ${PROG2}.c
# make typecard - quick overview of the basic types on the system
${PROG3}: ${PROG3}.c Makefile
${CC} ${CFLAGS} -o $@ ${PROG3}.c
./${PROG3}
install: all
${INSTALL} ${PROG1} ${BINDIR}
${INSTALL} ${PROG2} ${BINDIR}
clean:
${RM} ${PROG1} ${PROG2} ${PROG3} *.o *.p *.d *.s *.S *~ *.core .depend
# Simple procedure to speed up basic testing on multiple platforms
WF1 = top150adjectives.txt
WF2 = top400nouns.txt
WF3 = top1000.txt
WLIST = wordlist.txt
RESULTS = results.txt
test: all
./${PROG2} 8-16 ${WF1} 3-16 ${WF2} 3-16 ${WF3} 3-16 > ${WLIST}
./${PROG1} -vct4 -f ${WLIST} > ${RESULTS}
cleantest:
${RM} ${WLIST} ${RESULTS}
cleanall: clean cleantest

429
README

@ -0,0 +1,429 @@
About:
------
Eschalot is a TOR hidden service name generator, it allows one to produce
a (partially) customized vanity .onion address using a brute-force method.
See http://torproject.org for more information about the TOR network
and http://torproject.org/docs/hidden-services for the hidden services
documentation.
Why eschalot? Well, eschalot is a different name for shallot and it is
a fork of an older .onion names generator called shallot.
See http://github.com/katmagic/Shallot for information about the shallot
and also see the History section at the end of this document.
Eschalot is distributed in source form under BSD license. It should compile
on any Unix or Linux system, but might need some minor modifications.
It was developed and most extensively tested on OpenBSD, but was also tested
to compile and run on DragonFlyBSD, FreeBSD, CentOS Linux, and couple other
mainstream Linux distributions whose names I do not recall at the moment.
Various combinations of big/little endian platforms, 32bit/64bit platforms,
and gcc/pcc/llvm/clang static analizer were tested. Many bugs were uncovered,
some were fixed, some are still there - see TODO list if interested.
Compilation:
------------
Eschalot requires OpenSSL-0.9.7-or-later libraries with source headers.
You will also need a make utility (either BSD or GNU make will do) and
a C compiler (gcc, pcc, or llvm/clang).
Download the latest version of eschalot (currently eschalot-1.2.0), open a
terminal emulator, such as xterm, and change directory to where you saved
the eschalot-1.2.0.tar.gz archive (for examle /home/username/Download);
$ cd Download
$ tar xzvf eschalot-1.2.0.tar.gz
$ cd eschalot-1.2.0
$ make
To use a different (other than your system default) C compiler (such as pcc):
$ make clean
$ env CC=pcc make
If compilation fails, see some hints below under "Compilation Troubleshooting"
close to the end of this document.
If make succeeds, you might want to run a simple functionality test/demo with
$ make test
This will use the included worgen utility to create a test wordlist out of the
three small wordlists included with the distribution, will save the list to
'wordlist.txt', and will launch eschalot running with 4 threads to start
looking for the onion names with the prefixes in the wordlist.txt file.
The results will be redirected to the 'results.txt' file. This test needs
a fairly fast machine with at least 250Mb of RAM.
To remove the test files execute
$ make cleantest
To remove the compiled binaries execute
$ make clean
To cleanup everything execute
$ make cleanall
Example output from 'make test':
--------------------------------
$ make test
cc -std=c99 -O2 -fPIC -finline-functions -Wall -W -Wunused -pedantic -Wpointer-arith -Wreturn-type -Wstrict-prototypes -Wmissing-prototypes -Wshadow -Wcast-qual -Wextra -o eschalot eschalot.c -lpthread -lssl -lcrypto
cc -std=c99 -O2 -fPIC -finline-functions -Wall -W -Wunused -pedantic -Wpointer-arith -Wreturn-type -Wstrict-prototypes -Wmissing-prototypes -Wshadow -Wcast-qual -Wextra -o worgen worgen.c
./worgen 8-16 top150adjectives.txt 3-16 top400nouns.txt 3-16 top1000.txt 3-16 > wordlist.txt
Will be producing 8-16 character long word combinations.
Reading 3-16 characters words from top150adjectives.txt.
Reading 3-16 characters words from top400nouns.txt.
Reading 3-16 characters words from top1000.txt.
Loading words from top150adjectives.txt.
Loaded 150 words from top150adjectives.txt.
Loading words from top400nouns.txt.
Loaded 400 words from top400nouns.txt.
Loading words from top1000.txt.
Loaded 974 words from top1000.txt.
Working. 100% complete, 31122412 words (approximately 377Mb) produced.
Final count: 31366539 word combinations.
./eschalot -vct4 -f wordlist.txt > results.txt
Verbose, continuous, no digits, 4 threads, prefixes 8-16 characters long.
Reading words from wordlist.txt, please wait...
Loaded 31366539 words.
Sorting the word hashes and removing duplicates.
Final word count: 31363570.
Thread #1 started.
Thread #2 started.
Thread #3 started.
Thread #4 started.
Running, collecting performance data...
Found a key for acidfall (8) - acidfalleyt3kkva.onion
Total hashes: 131241356, running time: 10 seconds, hashes per second: 13124135
Found a key for redglass (8) - redglass6i2pxool.onion
Found a key for loudwalk (8) - loudwalk72kvhr4n.onion
Found a key for illarteye (9) - illarteyedjxf3pj.onion
Total hashes: 394606458, running time: 30 seconds, hashes per second: 13153548
Found a key for cutcolor (8) - cutcolorxqxz7ck4.onion
Found a key for safefold (8) - safefold7hmcigr7.onion
Found a key for tallidea (8) - tallideac5zyn3f7.onion
Found a key for wetactago (9) - wetactagot7b42kx.onion
Found a key for pooryear (8) - pooryearxutsizhe.onion
^C*** Signal SIGINT in eschalot-1.2.0 (test)
Usage:
------
Type
$ ./eschalot
and
$ ./worgen
without any options to get a quick usage information.
To search using 4 threads (if your CPU has 4 cores), in a verbose mode,
continuing to search after an .onion address is found, looking for a single
prefix "test":
$ ./eschalot -t4 -v -c -p test
or simply
$ ./eschalot -vct4 -p test
To search using a regular expression looking for names starting with "test"
or ending with "exam":
$ ./eschalot -vct4 -r "^test|exam$"
To search for a single prefix "hello" using one thread, redirecting the
output to a file named "results.txt", exiting after the first name is found:
$ ./eschalot -p hello >> results.txt
To search for prefixes from 8 to 10 characters long from a file named
"wordlist.txt" using 6 threads, in continuous and verbose mode,
redirecting the results to a file:
$ ./eschalot -vct6 -l8-10 -f wordlist.txt >> results.txt
Generating a wordlist:
----------------------
You can use the included utility "worgen" to generate large wordlists for
eschalot. This utility is far from complete and is not very user friendly,
but can be used if needed. To demonstrate by example:
Generate a (relatively small) list of 8 to 12 character long words by
mixing 3-10 character words from top1000.txt file, 3-6 character words
from top400nouns.txt, and 3-6 character words from top140adjectives.txt,
redirect the results to wordlist.txt:
$ ./worgen 8-12 top1000.txt 3-10 top400nouns.txt \
3-6 top150adjectives.txt 3-6 > wordlist.txt
Generate a large (~1.2Gb) file of 10 character long words by mixing twice
words from a single file:
$ ./worgen 10-10 nouns.txt 3-10 nouns.txt 3-10 > wordlist.txt
At this point you might want to try running
$ ./eschalot -vct6 -l 10-10 -f wordlist.txt > results.txt
to test if your system can load a large file into memory.
The result should look something like this:
$ ./eschalot -vct6 -l 10-10 -f wordlist.txt > results.txt
Verbose, continuous, no digits, 6 threads, prefixes 10-10 characters long.
Reading words from wordlist.txt, please wait...
Loaded 110792061 words.
Sorting the word hashes and removing duplicates.
Final word count: 110558812.
Thread #1 started.
Thread #2 started.
Thread #3 started.
Thread #4 started.
Thread #5 started.
Thread #6 started.
Running, collecting performance data...
Found a key for museumazof (10) - museumazofgsihx2.onion
Found a key for balzacnick (10) - balzacnickaxtbd4.onion
Found a key for methodmoor (10) - methodmooraudcft.onion
Found a key for gneissbutt (10) - gneissbuttieicps.onion
Found a key for todcorypha (10) - todcoryphadr7zv4.onion
Found a key for pleveniyar (10) - pleveniyarpa3hlx.onion
Found a key for caputwight (10) - caputwightz46r3n.onion
Found a key for mervensalp (10) - mervensalpskbwad.onion
Found a key for hallelenid (10) - hallelenidmhln6o.onion
Found a key for quotalysis (10) - quotalysisadbc57.onion
Found a key for longabarth (10) - longabarthvvdjpw.onion
Found a key for vannlozier (10) - vannlozierwqadcv.onion
Found a key for uriahcadre (10) - uriahcadreac7ujz.onion
Found a key for denmarkjew (10) - denmarkjewfyozqj.onion
Found a key for kochiiclod (10) - kochiiclodifftuw.onion
Found a key for fondusamba (10) - fondusambaialjro.onion
^C
As you see, it finds a lot of prefixes in just a few seconds, but most of them
are useless - that's the downside of using a really large wordlist with either
junk or extremely uncommon words combinations in it. Experiment with it! :)
Security of generated keys:
---------------------------
Original note from Shallot:
It is sometimes claimed that private keys generated by Shallot are less
secure than those generated by Tor. This is false. Although Shallot generates
a keypair with an unusually large public exponent e, it performs all of the
sanity checks specified by PKCS #1 v2.1 (directly in sane_key), and then
performs all of the sanity checks that Tor does when it generates an RSA
keypair (by calling the OpenSSL function RSA_check_key).
Eschalot additions:
Now the public exponent is limited to the range of
(0xFFFFFF + 2) to (0xFFFFFFFF) - basically, odd values that take at least,
and no more than, 4 bytes.
In addition, unlike shallot, after the RSA key has been finalized, the
.onion name is regenerated using the same procedure as used in the official
TOR client - this filters out the occasional bogus .onions that shallot
generated occasionally (and eschalot does too - this is a bug I have not
tracked down yet).
Now, there is nothing stopping the TOR developers from modifying the TOR
client to only accept manually imported keys with public exponent equal,
lets say, 65537 and nothing else, but that would be silly of them. It would
not improve TOR's performance much or serve any other purpose, but to
knock offline several well established hidden websites that have been using
shallot-generated keys for years. I would not worry about it.
Performance:
------------
Depends on how fast your CPU is and how many cores you have, but generally
speaking it's a bit faster than shallot. Up to twice as fast in some cases,
but it depends greatly on how fast the OpenSSL's SHA1 implementation is on
the system. Some use hand-optimized assembly, some use C versions.
Wordlist mode is obviously slower than a single fixed prefix mode, but not
by much. The difference between searching in a 100 words list and a 100 million
words list is negligible due to the binary search and hashed tree data
storage. Of course, that is if the whole wordlist fits in RAM completely.
Memory needed is approximately 0.5-0.7 of the size of the wordlist size
on disk (yes, eschalot needs less memory than the file takes due to the words
getting converted into binary format and stored in a sort of a hashed tree).
Compilation Troubleshooting:
----------------------------
1). Does the error message you are getting give you any hints?
2). If the error message complains that make/gmake/gcc/cc cannot be found,
you will need to install the make/gmake utility and gcc or some other C
compiler. Some of the Linuxes split the gcc package into several smaller ones
- you will need the one that says "GNU C Compiler" or something like that.
Note: most of the mainstream Linuxes do not come with a compiler by default
theese days even if you choose a complete - often 5-10Gb - installation.
(Yeah, that was a shock for me too), but it's fairly easy to install it by
using your operating system's software manager.
3). If it says something like "SHA1*** / RSA*** /BN_*** function not defined"
or "missing <openssl/***.h> header", you will need to make sure you not
only have the dynamic OpenSSL libraries installed, but also the header files.
On Linuxes, they are sometimes distributed in a different package from the
main OpenSSL and are called something like "OpenSSL-development" or
"OpenSSL-sources-and-headers" or something like that - look around.
4). If you get an error message about 'htobe32' function not being defined,
you can try using a locally-supplied copy by compiling with
$ env CFLAGS=-DNEED_HTOBE32 make
Same if your system does not have strnlen - try
$ env CFLAGS=-DNEED_STRNLEN make
Or might even have to define both like this:
$ env CFLAGS="-DNEED_HTOBE32 -DNEED_STRNLEN" make
5). If all of the above fails, take a look inside the Makefile, and see if
you need to disable or enable some additional C flags.
6). If your error message says something about endian.h, take a look at the
beginning of the eschalot.c file, see how that file is being included.
You might need to adjust it a bit (that part needs work - see TODO list).
7). If all else fails, send me an email or post something on the feedback
forum. I'll be happy to hear any feedback, positive or negative, and will try
to help.
Bugs and ToDo list:
-------------------
0). Highest priority bug:
Every so often, while searching in a wordlist mode, eschalot finds the
right prefix, but then, after finalizing the key and regenerating the .onion
name, the result is garbage. I suspected my CPU or RAM overheating at first,
but now I tend to think it's a bug in the program (or OpenSSL) somewhere.
It gets detected and rejected and a message is printed on STDERR, but it's
a big waste of hash cycles. Have to track it down.
1). worgen dumps core on 32-bit OpenBSD when using fairly large input
wordlists (triggers stack smash protection). Works fine on 64-bit systems.
2). I tried to optimize the main loop somewhat, but the wordlist loading
could use some improvement - realloc'ing 8 bytes at a time is slow (was
concerned about total memory used when loading large files when I did it).
3). Need better statistics with estimated time needed predictions.
4). Half the variables are global - does not hurt in this case, but is ugly.
5). Print out the public exponent used when a key is found.
6). Write a manpage.
7). Optimize and improve the worgen utility, it was a quick hack.
8). More testing on different OSes, finalize the htobe32/strnlen defines mess.
9). Attempt to implement a GPU hashing mode for Linux.
10). Add a local SHA1 function written in assembly for sparc/sparc64.
11). Make it compile on windows and provide windows binary.
12). Go over the numerous TODOs in the code and address them.
13). Generate one ultimate wordlist with good word combinations 8-16 chars
long, about 5-10Gb in size total, so it could be used to search for a
specific lengths even if the whole thing cannot fit in RAM at once. Perhaps
grab all the phrases and word combinations from a few hundred ebooks
instead of generating randomly mixed rubbish?
14). Move the defines, includes, and functions shared between eschalot and
worgen into "common.h/common.c" files.
15). Add a real self-test with fixed initial RSA key, compare a few hundred
generated .onion names to a known good file. Or something like that.
Make it all driven through the Makefile to simplify testing on different
platforms.
History:
--------
Circa 2006, a person with a nickname Cowboy Bebop created the original
onionhash-0.0.1, which evolved into onionhash-0.0.2 and 0.0.3, until Bebop
and his home at torlandypjxiligx.onion mysteriously vanished.
At this point, it was picked up by someone called Orum, who renamed the
onionhash to shallot and went through three versions until Orum's site at
hangman5naigg7rr.onion disappeared.
Another concerned OnionLand citizen Katmagic got shallot's sources from
taswebqlseworuhc.onion and put them into a Git repository. Made a few
modifications, wrote a new README, and put the whole thing up on GitHub.
I stumbled on the project at some point and had a few ideas on how to make
it more flexible. However, the changes I planned to make were too extensive
to consider simply patching shallot, so I decided to fork it and work on
it for my own private use. After messing with it (very) occasionally for
couple of months, I figured it might be of use to some other TOR enthusiasts,
even though I would not call my remake of shallot "production ready".
Initially I named my project "scallion", however, just a few days ago, I have
learned of yet another .onion names generator recently released which was,
unsurprisingly, named scallion, so I renamed my project to "eschalot".
See http://github.com/lachesis/scallion for more details on scallion.
It's all about choices and now you have several!
P.S. Following the tradition set forth by the previous authors, I will
remain anonymous for the time being.
P.P.S. Sending my greetings and thanks to all the people who worked on this
project before me and kept it alive over the years!
--Unperson Hiro
19 February 2013

@ -0,0 +1 @@
cloak

Binary file not shown.

@ -0,0 +1,791 @@
/* eschalot - generates vanity .onion names using brute-force hashing.
* A fork of shallot, which was a fork of onionhash. */
/*
* Copyright (c) 2013 Unperson Hiro <blacksunhq56imku.onion>
* Copyright (c) 2007 Orum <hangman5naigg7rr.onion>
* Copyright (c) 2006 Cowboy Bebop <torlandypjxiligx.onion>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
/* Lets agree on 100 characters line limit, tab is 8 spaces long, second level ident is 4 spaces. */
/* ---------- This wide ------------------------------------------------------------------------- */
#ifdef __linux__
# define _GNU_SOURCE
# include <endian.h>
#endif
#ifdef __FreeBSD__
# include <sys/endian.h>
#endif
#include <sys/types.h>
#include <ctype.h>
#include <getopt.h>
#include <inttypes.h>
#include <pthread.h>
#include <regex.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <openssl/bn.h>
#include <openssl/pem.h>
#include <openssl/rsa.h>
#include <openssl/sha.h>
/* Define NEED_HTOBE32 if htobe32() is not available on your platform. */
/* #define NEED_HTOBE32 */
#if BYTE_ORDER == LITTLE_ENDIAN
# ifdef NEED_HTOBE32
# define HTOBE32(x) (((uint32_t)(x) & 0xffu) << 24 | \
((uint32_t)(x) & 0xff00u) << 8 | \
((uint32_t)(x) & 0xff0000u) >> 8 | \
((uint32_t)(x) & 0xff000000u) >> 24)
# else
# define HTOBE32(x) htobe32(x)
# endif
#else
# define HTOBE32(x) (x)
#endif
/* Define NEED_STRNLEN if strnlen() is not available on your platform. */
/* #define NEED_STRNLEN */
#ifdef NEED_STRNLEN
static size_t strnlen(const char *, size_t);
size_t
strnlen(const char *s, size_t ml)
{
unsigned int i;
for (i = 0; *(s + i) != '\0' && i < ml; i++)
;
return i;
}
#endif
#define VERSION "1.2.0"
#define SHA_REL_CTX_LEN 10 * sizeof(SHA_LONG) /* 40 bytes */
#define RSA_KEYS_BITLEN 1024 /* RSA key length to use */
#define SIZE_OF_E 4 /* Limit public exponent to 4 bytes */
#define RSA_E_START 0xFFFFFFu + 2 /* Min e */
#define RSA_E_LIMIT 0xFFFFFFFFu /* Max e */
#define ONION_LENP1 17 /* Onion name length plus 1 */
#define MAX_THREADS 100 /* Maximum number of threads */
#define MAX_WORDS 0xFFFFFFFFu /* Maximum words to read from file */
#define BASE32_ALPHABET "abcdefghijklmnopqrstuvwxyz234567"
extern char *__progname;
/* Error and debug functions */
static void usage(void);
static void error(char *, ...);
static void verbose(char *, ...);
static void normal(char *, ...);
static void (*msg)(char *, ...);
/* User IO functions */
static void setoptions(int, char *[]);
static void readfile(void);
static void printresult(RSA *, uint8_t *, uint8_t *);
/* Math and search functions */
static _Bool fsearch(uint8_t *, uint8_t *);
static _Bool psearch(uint8_t *, uint8_t *);
static _Bool rsearch(uint8_t *, uint8_t *);
static _Bool (*search)(uint8_t *, uint8_t *);
static _Bool validkey(RSA *);
static signed int compare(const void *, const void *);
static void base32_enc(uint8_t *, uint8_t *);
static void base32_dec(uint8_t *, uint8_t *);
static void onion_enc(uint8_t *, RSA *);
static void zerobits(uint16_t * ind, uint64_t * word,
uint8_t * buffer, unsigned int length);
/* Main thread routine */
static void *worker(void *);
/* Global variables */
/* TODO: perhaps getting rid of so many globals is in order... */
_Bool done, cflag, fflag, nflag, pflag, rflag, vflag;
unsigned int minlen, maxlen, threads, prefixlen, wordcount;
char fn[FILENAME_MAX + 1], prefix[ONION_LENP1];
regex_t *regex;
struct {
unsigned int count;
uint64_t *branch;
} tree[65536] = { {0, NULL} };
int
main(int argc, char *argv[])
{
pthread_t babies[MAX_THREADS];
uint64_t count[MAX_THREADS];
unsigned int i, j, dupcount = 0;
/* Initialize global flags */
wordcount = done = cflag = fflag = nflag = pflag = rflag = vflag = 0;
minlen = 8;
maxlen = 16;
threads = 1;
msg = normal; /* Default: non-verbose */
search = NULL; /* No default search, has to be specified */
setoptions(argc, argv);
if (fflag) {
readfile();
msg("Sorting the word hashes and removing duplicates.\n");
wordcount = 0;
for (i = 0; i < 65536; i++) {
dupcount = 0;
qsort(tree[i].branch, tree[i].count, sizeof(tree[i].branch[0]), &compare);
for (j = 1; j < tree[i].count ; j++) {
if (tree[i].branch[j] == tree[i].branch[j - 1]) {
tree[i].branch[j - 1] = 0xFFFFFFFFFFFFFFFFu;
dupcount++;
}
}
if (dupcount > 0) {
qsort(tree[i].branch, tree[i].count, sizeof(tree[i].branch[0]),
&compare);
tree[i].count -= dupcount;
}
wordcount += tree[i].count;
}
msg("Final word count: %d.\n", wordcount);
}
/* Start our threads */
for (i = 1; i <= threads; i++) {
count[i] = 0;
if (pthread_create(&babies[i], NULL, worker, (void *)&count[i]) != 0)
error("Failed to start thread!\n");
msg("Thread #%d started.\n", i);
}
/* Monitor performance
* TODO: redo this whole thing, add estimate time for keys */
if (vflag) {
uint64_t loops;
time_t elapsed, start = time(NULL);
unsigned int delay = 5;
msg("Running, collecting performance data...\n");
for(;;) {
sleep(delay *= 2);
if (done)
return 0;
/* Collect our thread's counters */
loops = 0;
i = threads;
do {
loops += count[i];
} while (--i);
/* On a really slow machine the initial delay might not be
* enough to generate the first RSA key, so give it more time. */
if (loops == 0)
continue;
elapsed = time(NULL) - start;
/* Bug here somewhere - with pcc compiler only. */
/* TODO: Fix. */
msg("Total hashes: %" PRIu64
", running time: %d seconds, hashes per second: %" PRIu64 "\n",
loops, elapsed, (uint64_t)(loops / elapsed));
}
}
/* Wait for all the threads to exit */
for (i = 1; i <= threads; i++)
pthread_join(babies[i], NULL);
exit(0);
}
/* Main hashing thread */
void *
worker(void *arg)
{
SHA_CTX hash, copy;
RSA *rsa;
uint8_t *tmp, *der,
buf[SHA_DIGEST_LENGTH],
onion[ONION_LENP1],
onionfinal[ONION_LENP1];
signed int derlen;
uint64_t *counter;
/* Public exponent and the "big-endian" version of it */
unsigned int e, e_be;
counter = (uint64_t *)arg;
while (!done) {
/* Generate a new RSA key every time e reaches RSA_E_LIMIT */
rsa = RSA_generate_key(RSA_KEYS_BITLEN, RSA_E_START,
NULL, NULL);
if (!rsa)
error("RSA Key Generation failed!\n");
/* Too chatty - disable. */
/* msg("Generated a new RSA key pair.\n"); */
/* Encode RSA key in X.690 DER format */
if((derlen = i2d_RSAPublicKey(rsa, NULL)) < 0)
error("DER encoding failed!\n");
if ((der = tmp = (uint8_t *)malloc(derlen)) == NULL)
error("malloc(derlen) failed!\n");
if (i2d_RSAPublicKey(rsa, &tmp) != derlen)
error("DER encoding failed!\n");
/* Prepare the hash context */
SHA1_Init(&hash);
SHA1_Update(&hash, der, derlen - SIZE_OF_E);
free(der);
e = RSA_E_START - 2; /* public exponent */
/* Main loop */
while ((e < RSA_E_LIMIT) && !done) {
e += 2;
/* Convert e to big-endian format. */
e_be = HTOBE32(e);
/* Copy the relevant parts of already set up context. */
memcpy(&copy, &hash, SHA_REL_CTX_LEN); /* 40 bytes */
copy.num = hash.num;
/* Compute SHA1 digest (the real bottleneck) */
SHA1_Update(&copy, &e_be, SIZE_OF_E);
SHA1_Final(buf, &copy);
(*counter)++;
/* This is fairly fast, but can be faster if inlined. */
base32_enc(onion, buf);
/* The search speed varies based on the mode we are in.
* Regex's performance depends on the expression used.
* Fixed prefix is as fast as memcmp(3).
* Wordlist performance depends on (mostly):
* number of "lengths" to search for (-l from-to);
* number of unique words loaded from file.
*
* Inlining everything and optimizing for one mode and
* fixed word length improved the performance somewhat
* when I tried it, but it's not worth it. */
if (search(buf, onion)) {
/* Found a possible key,
* from here on down performance is not critical. */
if (!BN_bin2bn((uint8_t *)&e_be, SIZE_OF_E, rsa->e))
error("Failed to set e in RSA key!\n");
if (!validkey(rsa))
error("A bad key was found!\n");
if (pflag)
onion[prefixlen] = '\0';
onion_enc(onionfinal, rsa);
/* Every so often the onion found matches
* whatever we were looking for, but the final
* generated onion is garbage. I suspect a CPU
* or RAM overheating, but it could be a subtle
* bug somewhere. Hard to pin-point. According
* to the reports I've seen, shallot has had a
* similar problem.
*
* Happens most often in a wordlist search mode,
* but I think I have seen it in a regex mode
* as well. Does not seem to happen in a fixed
* prefix mode.
*
* Adding this check to avoid producing garbage
* results and to alleviate the problem a bit. */
if (strncmp((char *)onion, (char *)onionfinal,
(!rflag ? strnlen((char *)onion, ONION_LENP1) : 16))) {
msg("\nWARNING! Error detected! CPU/RAM overheating?\n");
msg("Found %s, but finalized to %s.\n",
(char *)onion, (char *)onionfinal);
msg("Suspending this thread for 30 seconds.\n");
sleep(30);
msg("Generating new RSA key.\n\n");
break;
} else
printresult(rsa, onion, onionfinal);
if (!cflag)
done = 1; /* Notify other threads. */
break;
}
}
RSA_free(rsa);
}
return 0;
}
/* Read words from file */
void
readfile(void)
{
FILE *file;
uint8_t w[ONION_LENP1] = { 0 }, buf[10];
uint8_t len, j;
uint16_t ind;
signed int c;
uint64_t wrd;
_Bool inword;
if ((file = fopen(fn,"r")) == NULL)
error("Failed to open %s!\n", fn);
msg("Reading words from %s, please wait...\n", fn);
/* We have to inspect each character individually anyway,
* so lets just use fgetc here and let the OS buffer stuff. */
j = 0;
while ((c = fgetc(file)) != EOF) {
c = tolower(c);
inword = 0;
/* Only load words with digits if the -n option was used. */
if ((c >= 'a' && c <= 'z') || (nflag && c >= '2' && c <= '7')) {
w[j++] = c;
inword = 1;
}
if ((!inword && j > 0) || j > 16) {
/* We clip the words longer than 16 characters here. */
w[j] = '\0';
j = 0;
/* Only pick the words of the length we need. */
len = strnlen((char *)w, ONION_LENP1);
if (len >= minlen && len <= maxlen && wordcount < MAX_WORDS) {
base32_dec(buf, w);
memset(w, 0, sizeof(w));
zerobits(&ind, &wrd, buf, len);
if ((tree[ind].branch = (uint64_t *)realloc(tree[ind].branch,
sizeof(uint64_t) * (tree[ind].count + 1))) == NULL)
error("realloc() failed!\n");
tree[ind].branch[tree[ind].count] = wrd;
tree[ind].count++;
wordcount++;
}
}
}
fclose(file);
if (wordcount == 0 )
error("Could not find any valid words in %s!\n", fn);
else
msg("Loaded %d words.\n", wordcount);
}
/* Check if the RSA key is ok (PKCS#1 v2.1). */
_Bool
validkey(RSA *rsa)
{
BN_CTX *ctx = BN_CTX_new();
BN_CTX_start(ctx);
BIGNUM *p1 = BN_CTX_get(ctx), /* p - 1 */
*q1 = BN_CTX_get(ctx), /* q - 1 */
*gcd = BN_CTX_get(ctx), /* GCD(p - 1, q - 1) */
*lambda = BN_CTX_get(ctx), /* LCM(p - 1, q - 1) */
*tmp = BN_CTX_get(ctx); /* temporary storage */
BN_sub(p1, rsa->p, BN_value_one()); /* p - 1 */
BN_sub(q1, rsa->q, BN_value_one()); /* q - 1 */
BN_gcd(gcd, p1, q1, ctx); /* gcd(p - 1, q - 1) */
BN_div(tmp, NULL, p1, gcd, ctx);
BN_mul(lambda, q1, tmp, ctx); /* lambda(n) */
/* Check if e is coprime to lambda(n). */
BN_gcd(tmp, lambda, rsa->e, ctx);
if (!BN_is_one(tmp)) {
verbose("WARNING: Key check failed - e is coprime to lambda!\n");
return 0;
}
/* Check if public exponent e is less than n - 1. */
/* Subtract n from e to avoid checking BN_is_zero. */
BN_sub(tmp, rsa->e, rsa->n);
if (!tmp->neg) {
verbose("WARNING: Key check failed - e is less than (n - 1)!\n");
return 0;
}
BN_mod_inverse(rsa->d, rsa->e, lambda, ctx); /* d */
BN_mod(rsa->dmp1, rsa->d, p1, ctx); /* d mod(p - 1) */
BN_mod(rsa->dmq1, rsa->d, q1, ctx); /* d mod(q - 1) */
BN_mod_inverse(rsa->iqmp, rsa->q, rsa->p, ctx); /* q ^ -1 mod p */
BN_CTX_end(ctx);
BN_CTX_free(ctx);
/* In theory this should never be true,
* unless the guy before me made a mistake ;). */
if (RSA_check_key(rsa) != 1) {
verbose("WARNING: OpenSSL's RSA_check_key(rsa) failed!\n");
return 0;
}
return 1;
}
/* Base32 encode 10 byte long 'src' into 16 character long 'dst' */
/* Experimental, unroll everything. So far, it seems to be the fastest of the
* algorithms that I've tried. TODO: review and decide if it's final.*/
void
base32_enc (uint8_t *dst, uint8_t *src)
{
dst[ 0] = BASE32_ALPHABET[ (src[0] >> 3) ];
dst[ 1] = BASE32_ALPHABET[((src[0] << 2) | (src[1] >> 6)) & 31];
dst[ 2] = BASE32_ALPHABET[ (src[1] >> 1) & 31];
dst[ 3] = BASE32_ALPHABET[((src[1] << 4) | (src[2] >> 4)) & 31];
dst[ 4] = BASE32_ALPHABET[((src[2] << 1) | (src[3] >> 7)) & 31];
dst[ 5] = BASE32_ALPHABET[ (src[3] >> 2) & 31];
dst[ 6] = BASE32_ALPHABET[((src[3] << 3) | (src[4] >> 5)) & 31];
dst[ 7] = BASE32_ALPHABET[ src[4] & 31];
dst[ 8] = BASE32_ALPHABET[ (src[5] >> 3) ];
dst[ 9] = BASE32_ALPHABET[((src[5] << 2) | (src[6] >> 6)) & 31];
dst[10] = BASE32_ALPHABET[ (src[6] >> 1) & 31];
dst[11] = BASE32_ALPHABET[((src[6] << 4) | (src[7] >> 4)) & 31];
dst[12] = BASE32_ALPHABET[((src[7] << 1) | (src[8] >> 7)) & 31];
dst[13] = BASE32_ALPHABET[ (src[8] >> 2) & 31];
dst[14] = BASE32_ALPHABET[((src[8] << 3) | (src[9] >> 5)) & 31];
dst[15] = BASE32_ALPHABET[ src[9] & 31];
dst[16] = '\0';
}
/* Decode base32 16 character long 'src' into 10 byte long 'dst'. */
/* TODO: Revisit and review, would like to shrink it down a bit.
* However, it has to stay endian-safe and be fast. */
void
base32_dec (uint8_t *dst, uint8_t *src)
{
uint8_t tmp[ONION_LENP1];
unsigned int i;
for (i = 0; i < 16; i++) {
if (src[i] >= 'a' && src[i] <= 'z') {
tmp[i] = src[i] - 'a';
} else {
if (src[i] >= '2' && src[i] <= '7')
tmp[i] = src[i] - '1' + ('z' - 'a');
else {
/* Bad character detected.
* This should not happen, but just in case
* we will replace it with 'z' character. */
tmp[i] = 26;
}
}
}
dst[0] = (tmp[ 0] << 3) | (tmp[1] >> 2);
dst[1] = (tmp[ 1] << 6) | (tmp[2] << 1) | (tmp[3] >> 4);
dst[2] = (tmp[ 3] << 4) | (tmp[4] >> 1);
dst[3] = (tmp[ 4] << 7) | (tmp[5] << 2) | (tmp[6] >> 3);
dst[4] = (tmp[ 6] << 5) | tmp[7];
dst[5] = (tmp[ 8] << 3) | (tmp[9] >> 2);
dst[6] = (tmp[ 9] << 6) | (tmp[10] << 1) | (tmp[11] >> 4);
dst[7] = (tmp[11] << 4) | (tmp[12] >> 1);
dst[8] = (tmp[12] << 7) | (tmp[13] << 2) | (tmp[14] >> 3);
dst[9] = (tmp[14] << 5) | tmp[15];
}
/* Print found .onion name and PEM formatted RSA key. */
void
printresult(RSA *rsa, uint8_t *target, uint8_t *actual)
{
uint8_t *dst;
BUF_MEM *buf;
BIO *b;
b = BIO_new(BIO_s_mem());
PEM_write_bio_RSAPrivateKey(b, rsa, NULL, NULL, 0, NULL, NULL);
BIO_get_mem_ptr(b, &buf);
(void)BIO_set_close(b, BIO_NOCLOSE);
BIO_free(b);
if ((dst = (uint8_t *)malloc(buf->length + 1)) == NULL)
error("malloc(buf->length + 1) failed!\n");
memcpy(dst, buf->data, buf->length);
dst[buf->length] = '\0';
msg("Found a key for %s (%d) - %s.onion\n",
target, strnlen((char *)target, ONION_LENP1), actual);
printf("----------------------------------------------------------------\n");
printf("%s.onion\n", actual);
printf("%s\n", dst);
fflush(stdout);
BUF_MEM_free(buf);
free(dst);
}
/* Generate .onion name from the RSA key. */
/* (using the same method as the official TOR client) */
void
onion_enc(uint8_t *onion, RSA *rsa)
{
uint8_t *bufa, *bufb, digest[SHA_DIGEST_LENGTH];
signed int derlen;
if((derlen = i2d_RSAPublicKey(rsa, NULL)) < 0)
error("DER encoding failed!\n");
if ((bufa = bufb = (uint8_t *)malloc(derlen)) == NULL)
error("malloc(derlen) failed!\n");
if (i2d_RSAPublicKey(rsa, &bufb) != derlen)
error("DER encoding failed!\n");
SHA1(bufa, derlen, digest);
free(bufa);
base32_enc(onion, digest);
}
/* Compare function for qsort(3). */
signed int
compare (const void *a, const void *b)
{
if (*((const uint64_t*)a) > *((const uint64_t*)b))
return 1;
if (*((const uint64_t*)a) < *((const uint64_t*)b))
return -1;
return 0;
}
/* Wordlist mode search. */
_Bool
fsearch(uint8_t *buf, uint8_t *onion)
{
unsigned int i;
uint16_t ind;
uint64_t wrd;
if (!nflag)
for (i = 0; i < minlen; i++)
if (onion[i] < 'a')
return 0;
for (i = minlen; i <= maxlen; i++) {
if (!nflag && onion[i - 1] < 'a')
return 0;
zerobits(&ind, &wrd, buf, i);
if (tree[ind].branch != NULL &&
bsearch(&wrd, tree[ind].branch, tree[ind].count,
sizeof(tree[ind].branch[0]), &compare)) {
onion[i] = '\0';
return 1;
}
}
return 0;
}
/* Regex mode search. */
_Bool
rsearch(__attribute__((unused)) uint8_t *buf, uint8_t *onion)
{
return !regexec(regex, (char *)onion, 0, 0, 0);
}
/* Fixed prefix mode search. */
_Bool
psearch(__attribute__((unused)) uint8_t *buf, uint8_t *onion)
{
return !memcmp(onion, prefix, prefixlen);
}
/* Zero unused bits, split 10 byte 'buffer' into 2 byte 'ind' and 8 byte 'word'. */
/* TODO: currently doing '1' fill instead of zeroes - decide if it's final. */
void
zerobits(uint16_t * ind, uint64_t * word, uint8_t * buffer, unsigned int length)
{
uint8_t bufcopy[10];
unsigned int usedbytes, usedbits;
memcpy(bufcopy, buffer, 10);
usedbytes = length * 5 / 8;
usedbits = length * 5 % 8;
if (usedbits) {
/* Lets try '1' fill instead of zeroes.. */
/* bufcopy[usedbytes] &= (0xFFu << (8 - usedbits)); */
bufcopy[usedbytes] |= (0xFFu >> usedbits);
usedbytes++;
}
if (usedbytes < 10) {
/* Lets try '1' fill instead of zeroes.. */
/* memset(&bufcopy[usedbytes], 0, 10 - usedbytes); */
memset(&bufcopy[usedbytes], 0xFFu, 10 - usedbytes);
}
memcpy(ind, &bufcopy[0], 2);
memcpy(word, &bufcopy[2], 8);
}
/* Read arguments and set global variables. */
void
setoptions(int argc, char *argv[])
{
int ch;
while ((ch = getopt(argc, argv, "cnvt:l:f:p:r:")) != -1)
switch (ch) {
case 'c':
cflag = 1;
break;
case 'n':
nflag = 1;
break;
case 'v':
vflag = 1;
msg = verbose;
break;
case 't':
threads = strtoul(optarg, NULL, 0);
if (threads < 1)
usage();
if (threads > MAX_THREADS)
threads = MAX_THREADS;
break;
case 'l':
minlen = strtoul(optarg, NULL, 0);
if (minlen < 8 || minlen > 16 || !strchr(optarg, '-'))
usage();
maxlen = strtoul(strchr(optarg, '-') + 1, NULL, 0);
if (maxlen < 8 || maxlen > 16 || minlen > maxlen)
usage();
break;
case 'f':
fflag = 1;
strncpy(fn, optarg, FILENAME_MAX);
search = fsearch;
break;
case 'p':
pflag = 1;
strncpy(prefix, optarg, ONION_LENP1);
minlen = maxlen = prefixlen = strnlen(prefix, ONION_LENP1);
if (prefixlen > 16 || prefixlen < 1)
usage();
for (unsigned int i = 0; i < prefixlen; i++) {
prefix[i] = tolower(prefix[i]);
if (!isalpha(prefix[i]) && (!nflag || !isdigit(prefix[i]) ||
prefix[i] < '2' || prefix[i] > '7'))
usage();
}
search = psearch;
break;
case 'r':
rflag = 1;
minlen = 1;
maxlen = 16;
if ((regex = (regex_t *)malloc(sizeof(regex_t))) == NULL)
error("malloc(sizeof(regex_t)) failed!\n");;
/* Do not use ICASE - too slow. */
if (regcomp(regex, optarg, REG_EXTENDED | REG_NOSUB))
error("Failed to compile regex expression!\n");
search = rsearch;
break;
default:
usage();
/* NOTREACHED */
}
if (fflag + rflag + pflag != 1)
usage();
msg("Verbose, ");
cflag ? msg("continuous, ") : msg("single result, ");
nflag ? msg("digits ok, ") : msg("no digits, ");
msg("%d threads, prefixes %d-%d characters long.\n",
threads, minlen, maxlen);
}
/* Print usage information and exit. */
void
usage(void)
{
fprintf(stderr,
"Version: %s\n"
"\n"
"usage:\n"
"%s [-c] [-v] [-t count] ([-n] [-l min-max] -f filename) | (-r regex) | (-p prefix)\n"
" -v : verbose mode - print extra information to STDERR\n"
" -c : continue searching after the hash is found\n"
" -t count : number of threads to spawn default is one)\n"
" -l min-max : look for prefixes that are from 'min' to 'max' characters long\n"
" -n : Allow digits to be part of the prefix (affects wordlist mode only)\n"
" -f filename: name of the text file with a list of prefixes\n"
" -p prefix : single prefix to look for (1-16 characters long)\n"
" -r regex : search for a POSIX-style regular expression\n"
"\n"
"Examples:\n"
" %s -cvt4 -l8-12 -f wordlist.txt >> results.txt\n"
" %s -v -r '^test|^exam'\n"
" %s -ct5 -p test\n\n",
VERSION, __progname, __progname, __progname, __progname);
fprintf(stderr,
" base32 alphabet allows letters [a-z] and digits [2-7]\n"
" Regex pattern examples:\n"
" xxx must contain 'xxx'\n"
" ^foo must begin with 'foo'\n"
" bar$ must end with 'bar'\n"
" b[aoeiu]r must have a vowel between 'b' and 'r'\n"
" '^ab|^cd' must begin with 'ab' or 'cd'\n"
" [a-z]{16} must contain letters only, no digits\n"
" ^dusk.*dawn$ must begin with 'dusk' and end with 'dawn'\n"
" [a-z2-7]{16} any name - will succeed after one iteration\n");
exit(1);
}
/* Spam STDERR with diagnostic messages... */
void
verbose(char *message, ...)
{
va_list ap;
va_start(ap, message);
vfprintf(stderr, message, ap);
va_end(ap);
fflush(stderr);
}
/* ...or be a very quiet thinker. */
void
normal(__attribute__((unused)) char *unused, ...)
{
}
/* Print error message and exit. */
/* (Not all Linuxes implement the err/errx functions properly.) */
void
error(char *message, ...)
{
va_list ap;
va_start(ap, message);
fprintf(stderr, "ERROR: ");
vfprintf(stderr, message, ap);
va_end(ap);
exit(1);
}

66894
nouns.txt

File diff suppressed because it is too large Load Diff

@ -0,0 +1,988 @@
Africa
America
British
England
English
Europe
France
French
God
Greek
Indian
Japanese
Washington
able
about
above
across
act
action
actually
add
addition
adjective
afraid
after
again
against
age
ago
agreed
ahead
air
all
allow
almost
alone
along
already
also
although
always
among
amount
and
angle
animal
another
answer
any
anything
appear
apple
are
area
arms
army
around
arrived
art
ask
ass
away
baby
back
bad
ball
bank
base
bear
beat
beautiful
became
because
become
bed
been
before
began
begin
behind
being
believe
bell
belong
below
beside
best
better
between
big
bill
birds
bit
black
block
blood
blow
blue
board
boat
body
bones
book
boris
born
both
bottom
box
boy
branches
break
bright
bring
broken
brother
brought
brown
bruce
build
building
built
burning
business
but
buy
call
came
can
cannot
capital
captain
car
care
carefully
carry
case
cat
catch
cattle
caught
cause
cells
center
cents
century
certain
chance
change