the input file is in an encrypted format, then the output file must be
the same format (decrypted). To decrypt such a file, specify the
encrypted file as @var{input}. The output will be the equivalent
-plaintext file. You will be prompted for the password (or use
-@option{-p}, documented below).
+plaintext file.
+
+The password for encrypted files can be specified a few different
+ways. If the password is known, use the @option{-p} option
+(documented below) or allow @command{pspp-convert} to prompt for it.
+If the password is unknown, use the @option{-a} and @option{-l}
+options to specify how to search for it.
Use @code{-O @var{extension}} to override the inferred format or to
specify the format for unrecognized extensions.
Be aware that command-line options, including passwords, may be
visible to other users on multiuser systems.
+When used with @option{-a} (or @option{--password-alphabet}) and
+@option{-l} (or @option{--password-length}), this option specifies the
+starting point for the search. This can be used to restart a search
+that was interrupted.
+
+@item -a @var{alphabet}
+@item --password-alphabet=@var{alphabet}
+Specifies the alphabet of symbols over which to search for an
+encrypted file's password. @var{alphabet} may include individual
+characters and ranges delimited by @samp{-}. For example, @option{-a
+a-z} searches lowercase letters, @option{-a A-Z0-9} searches uppercase
+letters and digits, and @option{-a ' -~'} searches all printable ASCII
+characters.
+
+@item -l @var{max-length}
+@item --password-length=@var{max-length}
+Specifies the maximum length of the passwords to try.
+
@item -h
@itemx --help
Prints a usage message on stdout and exits.
int Nr;
};
-static bool try_password(struct encrypted_file *, const char *password);
static bool decode_password (const char *input, char output[11]);
static bool fill_buffer (struct encrypted_file *);
{
char decoded_password[11];
- return (try_password (f, password)
+ return (encrypted_file_unlock__ (f, password)
|| (decode_password (password, decoded_password)
- && try_password (f, decoded_password)));
+ && encrypted_file_unlock__ (f, decoded_password)));
}
/* Attempts to read N bytes of plaintext from F into BUF. Returns the number
return true;
}
-/* If CIPHERTEXT is the first ciphertext block in an encrypted .sav file for
- PASSWORD, initializes rk[] and returns an nonzero Nr value.
-
- Otherwise, returns zero. */
-static bool
-try_password(struct encrypted_file *f, const char *password)
+/* Attempts to use plaintext password PASSWORD to unlock F. Returns true if
+ successful, otherwise false. */
+bool
+encrypted_file_unlock__ (struct encrypted_file *f, const char *password)
{
/* NIST SP 800-108 fixed data. */
static const uint8_t fixed[] = {
static bool decrypt_file (struct encrypted_file *enc,
const struct file_handle *input_filename,
const struct file_handle *output_filename,
- const char *password);
+ const char *password,
+ const char *alphabet, int max_length);
int
main (int argc, char *argv[])
struct file_handle *output_fh = NULL;
struct casewriter *writer;
const char *password = NULL;
+ struct string alphabet = DS_EMPTY_INITIALIZER;
+ int length = 0;
long long int i;
{
{ "cases", required_argument, NULL, 'c' },
{ "encoding", required_argument, NULL, 'e' },
+
{ "password", required_argument, NULL, 'p' },
+ { "password-alphabet", required_argument, NULL, 'a' },
+ { "password-length", required_argument, NULL, 'l' },
{ "output-format", required_argument, NULL, 'O' },
int c;
- c = getopt_long (argc, argv, "c:e:p:O:hv", long_options, NULL);
+ c = getopt_long (argc, argv, "c:e:p:a:l:O:hv", long_options, NULL);
if (c == -1)
break;
password = optarg;
break;
+ case 'l':
+ length = atoi (optarg);
+ break;
+
+ case 'a':
+ for (const char *p = optarg; *p; )
+ if (p[1] == '-' && p[2] > p[0])
+ {
+ for (int ch = p[0]; ch <= p[2]; ch++)
+ ds_put_byte (&alphabet, ch);
+ p += 3;
+ }
+ else
+ ds_put_byte (&alphabet, *p++);
+ break;
+
case 'O':
output_format = optarg;
break;
"format"));
}
- if (! decrypt_file (enc, input_fh, output_fh, password))
+ if (!decrypt_file (enc, input_fh, output_fh, password,
+ ds_cstr (&alphabet), length))
goto error;
goto exit;
error (1, 0, _("%s: error writing output file"), output_filename);
exit:
+ ds_destroy (&alphabet);
dict_unref (dict);
fh_unref (output_fh);
fh_unref (input_fh);
return 0;
error:
+ ds_destroy (&alphabet);
dict_unref (dict);
fh_unref (output_fh);
fh_unref (input_fh);
decrypt_file (struct encrypted_file *enc,
const struct file_handle *ifh,
const struct file_handle *ofh,
- const char *password)
+ const char *password,
+ const char *alphabet,
+ int max_length)
{
FILE *out;
int err;
const char *input_filename = fh_get_file_name (ifh);
const char *output_filename = fh_get_file_name (ofh);
- if (password == NULL)
+ if (alphabet[0] && max_length)
{
- password = getpass ("password: ");
- if (password == NULL)
- return false;
+ size_t alphabet_size = strlen (alphabet);
+ char *pw = xmalloc (max_length + 1);
+ int *indexes = xzalloc (max_length * sizeof *indexes);
+
+ for (int len = password ? strlen (password) : 0;
+ len <= max_length; len++)
+ {
+ if (password && len == strlen (password))
+ {
+ for (int i = 0; i < len; i++)
+ {
+ const char *p = strchr (alphabet, password[i]);
+ if (!p)
+ error (1, 0, _("%s: '%c' is not in alphabet"),
+ password, password[i]);
+ indexes[i] = p - alphabet;
+ pw[i] = *p;
+ }
+ }
+ else
+ {
+ memset (indexes, 0, len * sizeof *indexes);
+ for (int i = 0; i < len; i++)
+ pw[i] = alphabet[0];
+ }
+ pw[len] = '\0';
+
+ unsigned int target = 0;
+ for (unsigned int j = 0; ; j++)
+ {
+ if (j >= target)
+ {
+ target += 100000;
+ if (isatty (STDOUT_FILENO))
+ {
+ printf ("\rlength %d: %s", len, pw);
+ fflush (stdout);
+ }
+ }
+ if (encrypted_file_unlock__ (enc, pw))
+ {
+ printf ("\npassword is: \"%s\"\n", pw);
+ password = pw;
+ goto success;
+ }
+
+ int i;
+ for (i = 0; i < len; i++)
+ if (++indexes[i] < alphabet_size)
+ {
+ pw[i] = alphabet[indexes[i]];
+ break;
+ }
+ else
+ {
+ indexes[i] = 0;
+ pw[i] = alphabet[indexes[i]];
+ }
+ if (i == len)
+ break;
+ }
+ }
+ free (indexes);
+ free (pw);
+
+ success:;
}
+ else
+ {
+ if (password == NULL)
+ {
+ password = getpass ("password: ");
+ if (password == NULL)
+ return false;
+ }
- if (!encrypted_file_unlock (enc, password))
- error (1, 0, _("sorry, wrong password"));
+ if (!encrypted_file_unlock (enc, password))
+ error (1, 0, _("sorry, wrong password"));
+ }
out = fn_open (ofh, "wb");
if (out == NULL)