You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 

557 lines
12 KiB

/* (C) folkert van heusden
* released under AGPL v3.0
*/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <time.h>
#include <string.h>
#include <signal.h>
#include <errno.h>
#include <ctype.h>
#include <gpgme.h>
#define MODE_RANDOM 1
#define MODE_INCREMENTAL 2
#define MODE_FILE 3
#define MAX_PP_LEN 128
unsigned char passphrase[MAX_PP_LEN + 2];
unsigned char passphrase_template[MAX_PP_LEN + 2];
int min_length = 1;
int max_length = MAX_PP_LEN;
char *pp_file_in = NULL;
FILE *pp_file_in_fh = NULL;
long pp_file_in_offset = 0; /* this kludge is to make sure we have the file-
* offset *before* the current passphrase as we
* might get the signal while the current one
* still is being processed
*/
char *pp_file_out = NULL;
unsigned char charset[256];
int charset_n = 1;
int mode = MODE_INCREMENTAL;
/* int mode = MODE_RANDOM; */
long long int n_done = 0;
void error_exit(char *str, gpgme_error_t err)
{
fprintf(stderr, "%s: %s\n", str, gpgme_strerror(err));
exit(1);
}
char valtohexdigit(int n)
{
if (n > 9)
return 'A' + (n - 10);
return '0' + n;
}
int hexdigittoval(char hex)
{
hex = toupper(hex);
if (hex >= 'A')
return hex - 'A' + 10;
return hex - '0';
}
int cvt_from_hex_string(char *in, char *out)
{
int loop, len = strlen(in), index = 0;
for(loop=0; loop<len; loop+=2)
{
out[index++] = (hexdigittoval(in[loop]) << 4) + hexdigittoval(in[loop + 1]);
}
return index;
}
void sighandler(int sig)
{
FILE *fh = fopen("nasty.state", "w");
if (fh)
{
int loop, len;
fprintf(fh, "# Do not edit this file (unless you're really sure what you're doing)!\n\n");
fprintf(fh, "mode=%d\n", mode);
len = strlen((char *)passphrase_template);
fprintf(fh, "cur=");
for(loop=0; loop<len; loop++)
fprintf(fh, "%c%c", valtohexdigit(passphrase_template[loop] >> 4), valtohexdigit(passphrase_template[loop] & 15));
fprintf(fh, "\n");
fprintf(fh, "charset=");
for(loop=0; loop<charset_n; loop++)
fprintf(fh, "%c%c", valtohexdigit(charset[loop] >> 4), valtohexdigit(charset[loop] & 15));
fprintf(fh, "\n");
fprintf(fh, "min_length=%d\n", min_length);
fprintf(fh, "max_length=%d\n", max_length);
fprintf(fh, "n_done=%lld\n", n_done);
if (mode == MODE_FILE)
{
fprintf(fh, "file=%s\n", pp_file_in);
fprintf(fh, "file_offset=%ld\n", pp_file_in_offset);
}
fclose(fh);
printf("\nState saved to 'nasty.state'\n");
exit(1);
}
}
void not_found(void)
{
if (pp_file_out)
{
FILE *fh = fopen(pp_file_out, "w");
if (fh)
{
fprintf(fh, "passphrase_not_found\n");
fclose(fh);
}
else
fprintf(stderr, "Failed to create file '%s': %s\n", pp_file_out, strerror(errno));
}
else
{
printf("Passphrase not found.\n");
}
exit(1);
}
/* Request a passphrase from the user. */
gpgme_error_t passphrase_cb(void *hook, const char *uid_hint, const char *passphrase_info, int prev_was_bad, int fd)
{
int loop, len = 0;
if (mode == MODE_INCREMENTAL)
{
int index = 0;
for(;;)
{
if (passphrase_template[index] < (charset_n - 1))
{
passphrase_template[index]++;
break;
}
else
{
passphrase_template[index] = 1;
index++;
if (index == max_length)
not_found();
}
}
len = strlen((char *)passphrase_template);
}
else if (mode == MODE_RANDOM)
{
do
{
len = (rand() % (max_length - min_length)) + min_length;
}
while(len < min_length || len > max_length);
for(loop=0; loop<len; loop++)
{
passphrase_template[loop] = (rand() % (charset_n - 1)) + 1;
}
passphrase_template[len] = 0x00;
}
else if (mode == MODE_FILE)
{
pp_file_in_offset = ftell(pp_file_in_fh);
char c;
int idx = 0;
while ((c=fgetc(pp_file_in_fh)) != '\n'
&& c != -1
&& idx < MAX_PP_LEN) {
passphrase[idx] = c;
++idx;
}
passphrase[idx] = '\0';
if (idx == 0) {
not_found();
exit(1);
}
}
if (mode != MODE_FILE)
{
for(loop=0; loop<len; loop++)
passphrase[loop] = charset[passphrase_template[loop]];
passphrase[loop] = 0x00;
}
n_done++;
strcat((char *)passphrase, "\n");
write(fd, (char *)passphrase, strlen((char *)passphrase));
return 0;
}
void open_pp_file_in(void)
{
pp_file_in_fh = fopen(pp_file_in, "r");
if (!pp_file_in_fh)
{
printf("Failed to open passphrase-file %s: %s\n", pp_file_in, strerror(errno));
exit(1);
}
}
int load_state(void)
{
int ok = 0;
FILE *fh = fopen("nasty.state", "r");
if (fh)
{
char buffer[4096], *dummy;
while(!feof(fh))
{
memset(buffer, 0x00, 4096);
if (!fgets(buffer, 4096, fh))
break;
dummy = strchr(buffer, '\n');
if (dummy) *dummy = 0x00;
if (buffer[0] == '#' || buffer[0] == ';' || strlen(buffer) == 0)
continue;
dummy = strchr(buffer, '=');
if (!dummy) goto state_file_corrupted;
*dummy = 0x00;
dummy++;
if (strcmp(buffer, "cur") == 0)
{
int len = cvt_from_hex_string(dummy, (char *)passphrase_template);
passphrase_template[len] = 0x00;
passphrase_template[0]--; /* make sure that it is fully checked */
}
else if (strcmp(buffer, "charset") == 0)
{
charset_n = cvt_from_hex_string(dummy, (char *)charset);
}
else if (strcmp(buffer, "min_length") == 0)
{
min_length = atoi(dummy);
}
else if (strcmp(buffer, "max_length") == 0)
{
max_length = atoi(dummy);
}
else if (strcmp(buffer, "n_done") == 0)
{
n_done = atoll(dummy);
}
else if (strcmp(buffer, "mode") == 0)
{
mode = atoi(dummy);
}
else if (strcmp(buffer, "file") == 0)
{
pp_file_in = strdup(dummy);
if (!pp_file_in)
{
printf("Memory allocation failure.\n");
exit(1);
}
open_pp_file_in();
}
else if (strcmp(buffer, "file_offset") == 0)
{
if (!pp_file_in_fh) goto state_file_corrupted;
if (fseek(pp_file_in_fh, atol(dummy), SEEK_SET) == -1)
{
printf("Failed to seek in passphrase-file: %s\n", strerror(errno));
exit(1);
}
}
else
{
state_file_corrupted:
fprintf(stderr, "nasty.state is corrupted!\n");
fprintf(stderr, "(%s=%s is not understood)\n", buffer, dummy);
exit(1);
}
}
fclose(fh);
}
else
{
ok = -1;
}
if (ok == 0)
printf("Using state from previous run\n");
return ok;
}
void add_charset_range(int start, int end)
{
int loop;
for(loop=start; loop<=end; loop++)
{
charset[charset_n++] = (unsigned char)loop;
}
}
void usage(void)
{
fprintf(stderr, "-a x set minimum length of passphrase\n");
fprintf(stderr, "-b x set maximum length\n");
fprintf(stderr, "-m x set guessing mode:\n");
fprintf(stderr, " incremental: try them all\n");
fprintf(stderr, " random: try at random\n");
fprintf(stderr, " file: read phrases from file (use -i)\n");
fprintf(stderr, "-i x file to read the passphrases from\n");
fprintf(stderr, "-f x file to write the found passphrase to\n");
fprintf(stderr, "-c x... charset, one or more from the following:\n");
fprintf(stderr, " a: a-z\n");
fprintf(stderr, " A: A-Z\n");
fprintf(stderr, " 0: 0-9\n");
fprintf(stderr, " .: all ascii values (32...126)\n");
fprintf(stderr, " +: 32...255 (default(!))\n");
fprintf(stderr, "-k x filter string to select a key\n");
}
void filter_keys_and_select_first(gpgme_ctx_t context, const char *key_filter_string, gpgme_key_t *selected_key)
{
gpgme_error_t err;
gpgme_key_t key;
err = gpgme_op_keylist_start(context, key_filter_string, 1);
if (err != GPG_ERR_NO_ERROR) error_exit("gpgme_op_keylist_start failed", err);
err = gpgme_op_keylist_next(context, &key);
if (err != GPG_ERR_NO_ERROR) error_exit("gpgme_op_keylist_next failed", err);
err = gpgme_op_keylist_end(context);
if (err != GPG_ERR_NO_ERROR) error_exit("gpgme_op_keylist_end failed", err);
err = gpgme_signers_add(context, key);
if (err != GPG_ERR_NO_ERROR) error_exit("gpgme_signers_add failed", err);
gpgme_key_unref(key);
printf ("Using %s:", key->subkeys->keyid);
if (key->uids && key->uids->name) printf(" %s", key->uids->name);
if (key->uids && key->uids->email) printf(" <%s>", key->uids->email);
putchar ('\n');
*selected_key = key;
}
int main(int argc, char *argv[])
{
gpgme_ctx_t ctx;
gpgme_error_t err;
gpgme_data_t in, out;
time_t start, then = time(NULL);
int c, loop;
char charset_set = 0;
char *key_filter_string = NULL;
printf("nasty v" VERSION ", (C) 2005-2017 by folkert@vanheusden.com\n\n");
start = then;
srand(then);
signal(SIGPIPE, SIG_IGN);
memset(passphrase, 0, min_length);
memset(passphrase_template, 0, min_length);
charset[0] = 0x00;
charset_n = 1;
if (load_state() == 0)
{
if (argc > 1)
{
fprintf(stderr, "Cannot use commandline switches when using state-file from a previous run.\n");
fprintf(stderr, "Delete 'nasty.state' to start with other parameters.\n");
return 1;
}
goto skip_switches;
}
while((c = getopt(argc, argv, "a:b:m:c:f:i:k:h")) != -1)
{
switch(c)
{
case 'a':
min_length = atoi(optarg);
if (min_length < 1)
{
fprintf(stderr, "Min. passphrase length is 1\n");
return 1;
}
break;
case 'b':
max_length = atoi(optarg);
if (max_length > MAX_PP_LEN)
{
fprintf(stderr, "Max. passphrase length is %d\n", MAX_PP_LEN);
return 1;
}
break;
case 'm':
if (strcasecmp(optarg, "incremental") == 0)
mode = MODE_INCREMENTAL;
else if (strcasecmp(optarg, "random") == 0)
mode = MODE_RANDOM;
else if (strcasecmp(optarg, "file") == 0)
mode = MODE_FILE;
else
{
fprintf(stderr, "%s: unknown mode\n", optarg);
return 1;
}
break;
case 'c':
for(loop=0; loop<strlen(optarg); loop++)
{
if (optarg[loop] == 'a')
add_charset_range('a', 'z');
else if (optarg[loop] == 'A')
add_charset_range('A', 'Z');
else if (optarg[loop] == '0')
add_charset_range('0', '9');
else if (optarg[loop] == '.')
add_charset_range(32, 126);
else if (optarg[loop] == '+')
add_charset_range(32, 255);
else
{
fprintf(stderr, "-c: %c is not understood\n", optarg[loop]);
return 1;
}
charset_set = 1;
}
break;
case 'f':
pp_file_out = optarg;
break;
case 'i':
pp_file_in = optarg;
break;
case 'k':
key_filter_string = optarg;
break;
case '?':
case 'h':
usage();
return 1;
}
}
if (!charset_set)
{
/* set default charset */
add_charset_range(32, 255);
}
if (mode == MODE_FILE)
{
if (pp_file_in == NULL)
{
fprintf(stderr, "Please select the file to read the passphrases from with '-i'.\n");
return 1;
}
open_pp_file_in();
}
skip_switches:
signal(SIGTERM, sighandler);
signal(SIGINT, sighandler);
signal(SIGKILL, sighandler);
(void)gpgme_check_version(NULL);
err = gpgme_new(&ctx);
if (err != GPG_ERR_NO_ERROR) error_exit("gpgme_new failed", err);
err = gpgme_data_new_from_mem(&in, "1234", 4, 0);
if (err != GPG_ERR_NO_ERROR) error_exit("gpgme_data_new_from_mem failed", err);
err = gpgme_data_new(&out);
if (err != GPG_ERR_NO_ERROR) error_exit("gpgme_data_new failed", err);
gpgme_set_passphrase_cb(ctx, passphrase_cb, NULL);
gpgme_key_t selected_key;
filter_keys_and_select_first(ctx, key_filter_string, &selected_key);
do
{
err = gpgme_op_sign(ctx, in, out, GPGME_SIG_MODE_DETACH);
if ((time(NULL) - then) > 2)
{
then = time(NULL);
printf("# tried: %lld (%f per second), last tried: %s\r", n_done, n_done / (double)(then - start), passphrase);
fflush(stdout);
}
}
while(err != 0);
gpgme_key_unref(selected_key);
if (err == 0)
printf("Passphrase is: %s\n", passphrase);
else
error_exit("gpgme_op_sign failed", err);
if (pp_file_out)
{
FILE *fh = fopen(pp_file_out, "w");
if (fh)
{
fprintf(fh, "passphrase=%s\n", passphrase);
fclose(fh);
}
else
fprintf(stderr, "Failed to create file '%s': %s\n", pp_file_out, strerror(errno));
}
(void)unlink("nasty.state");
return 0;
}