Add -H, --sha1 option, to allow computing reproducible passwords,

given a known file, and a known seed.  (ie: pwgen -H
~/my_favourite.mp3#olivier@guerrier.com gives me a list of possibles
passwords for my pop3 account, and I can ask this list again and
again).  Feature suggested and implemented by Olivier Guerrier
<olivier@guerrier.com>.
This commit is contained in:
tytso 2005-06-14 22:35:55 +00:00
parent 02ab9c8172
commit 5f96a6ea2d
12 changed files with 560 additions and 24 deletions

View File

@ -1,3 +1,12 @@
2005-06-14 Theodore Ts'o <tytso@mit.edu>
* pwgen.c: Add -H, --sha1 option, to allow computing reproducible
passwords, given a known file, and a known seed.
(ie: pwgen -H ~/my_favourite.mp3#olivier@guerrier.com gives me
a list of possibles passwords for my pop3 account, and I can
ask this list again and again). Feature suggested and
implemented by Olivier Guerrier <olivier@guerrier.com>.
2005-06-13 Theodore Ts'o <tytso@mit.edu>
* Fix obvious spelling typo in pwgen.1. (Addresses Debian Bug

View File

@ -31,9 +31,9 @@ all:: pwgen
.c.o:
$(CC) -c $(ALL_CFLAGS) $< -o $@
OBJS= pwgen.o pw_phonemes.o pw_rand.o randnum.o
OBJS= pwgen.o pw_phonemes.o pw_rand.o randnum.o sha1.o sha1num.o
SRCS= pwgen.c pw_phonemes.c pw_rand.c randnum.c
SRCS= pwgen.c pw_phonemes.c pw_rand.c randnum.c sha1.c sha1num.c
pwgen: $(OBJS)
@ -127,3 +127,5 @@ pwgen.o: pwgen.c pwgen.h
pw_phonemes.o: pw_phonemes.c pwgen.h
pw_rand.o: pw_rand.c pwgen.h
randnum.o: randnum.c pwgen.h
sha1.o: sha1.c sha1.h
sha1num.o: sha1num.c sha1.h pwgen.h

2
debian/changelog vendored
View File

@ -3,6 +3,8 @@ pwgen (2.03-2) unstable; urgency=low
* Adopt maintainership of pwgen. (Closes: #282076)
* Fix minor bug in man page. (Closes: #311461)
* Convert from debmake to debhelper
* Add the --sha1 option so that pwgen uses the SHA1 hash to generate
(not so) random passwords.
-- Theodore Y. Ts'o <tytso@mit.edu> Tue, 14 Jun 2005 14:59:19 -0400

View File

@ -69,10 +69,10 @@ try_again:
should_be = 0;
first = 1;
should_be = pw_random_number(2) ? VOWEL : CONSONANT;
should_be = pw_number(2) ? VOWEL : CONSONANT;
while (c < size) {
i = pw_random_number(NUM_ELEMENTS);
i = pw_number(NUM_ELEMENTS);
str = elements[i].str;
len = strlen(str);
flags = elements[i].flags;
@ -98,7 +98,7 @@ try_again:
/* Handle PW_ONE_CASE */
if (feature_flags & PW_ONE_CASE) {
if ((first || flags & CONSONANT) &&
(pw_random_number(10) < 3)) {
(pw_number(10) < 3)) {
buf[c] = toupper(buf[c]);
feature_flags &= ~PW_ONE_CASE;
}
@ -114,14 +114,14 @@ try_again:
* Handle PW_ONE_NUMBER
*/
if (feature_flags & PW_ONE_NUMBER) {
if (!first && (pw_random_number(10) < 3)) {
buf[c++] = pw_random_number(10)+'0';
if (!first && (pw_number(10) < 3)) {
buf[c++] = pw_number(10)+'0';
buf[c] = 0;
feature_flags &= ~PW_ONE_NUMBER;
first = 1;
prev = 0;
should_be = pw_random_number(2) ?
should_be = pw_number(2) ?
VOWEL : CONSONANT;
continue;
}
@ -135,7 +135,7 @@ try_again:
} else { /* should_be == VOWEL */
if ((prev & VOWEL) ||
(flags & DIPTHONG) ||
(pw_random_number(10) > 3))
(pw_number(10) > 3))
should_be = CONSONANT;
else
should_be = VOWEL;

View File

@ -20,7 +20,7 @@ void pw_rand(char *buf, int size, int pw_flags)
len = strlen(chars);
while (i < size) {
ch = chars[pw_random_number(len)];
ch = chars[pw_number(len)];
buf[i++] = ch;
}
buf[size] = 0;

14
pwgen.1
View File

@ -71,6 +71,20 @@ only be used for machine passwords, since otherwise it's almost
guaranteed that users will simply write the password on a piece of
paper taped to the monitor...
.TP
.B \-H, --sha1=\fI/path/to/file[#seed]
Will use the sha1's hash of given file and the optional seed to create
password. It will allow you to compute the same password later,
if you remember the file, seed, and pwgen's options used.
ie: pwgen -H ~/your_favourite.mp3#your@email.com gives
a list of possibles passwords for your pop3 account, and you can
ask this list again and again.
.IP
.B WARNING:
The passwords generated using this option are not very random. If you use
this option, make sure the attacker can not obtain a copy of the file.
Also, note that the name of the file may be easily available from the
~/.history or ~/.bash_history file.
.TP
.B \-h, --help
Print a help message.
.TP

17
pwgen.c
View File

@ -18,11 +18,8 @@
#include "pwgen.h"
struct pwgen_func generators[] = {
{ "phonemes", pw_phonemes, 0 },
{ "rand", pw_rand, 0 },
{ 0, 0, 0 }
};
/* Globals variables */
int (*pw_number)(int max_num);
/* Program parameters set via getopt */
@ -42,6 +39,7 @@ struct option pwgen_options[] = {
{ "help", no_argument, 0, 'h'},
{ "no-numerals", no_argument, &numeric_flag, 0 },
{ "no-capitalize", no_argument, &case_flag, 0 },
{ "sha1", required_argument, 0, 'H' },
{ 0, 0, 0, 0}
};
#endif
@ -59,6 +57,8 @@ const char *usage_msg =
"\tPrint a help message\n"
" --no-numerals, --no-capitalize\n"
"\tDon't include a number or capital letter in the password\n"
" -H or --sha1=path/to/file[#seed]\n"
"\tUse sha1 hash of given file as a (not so) random generator\n"
" -C\n\tPrint the generated passwords in columns\n"
" -1\n\tDon't print the generated passwords in columns\n"
;
@ -79,6 +79,7 @@ int main(int argc, char **argv)
void (*pwgen)(char *inbuf, int size, int pw_flags);
pwgen = pw_phonemes;
pw_number = pw_random_number;
if (isatty(1)) {
do_columns = 1;
case_flag = PW_ONE_CASE;
@ -87,7 +88,7 @@ int main(int argc, char **argv)
while (1) {
#ifdef HAVE_GETOPT_LONG
c = getopt_long(argc, argv, "1aCcnN:sh", pwgen_options, 0);
c = getopt_long(argc, argv, "1aCcnN:shH:", pwgen_options, 0);
#else
c = getopt(argc, argv, "1aCcnN:sh");
#endif
@ -120,6 +121,10 @@ int main(int argc, char **argv)
case '1':
do_columns = 0;
break;
case 'H':
pw_sha1_init(optarg);
pw_number = pw_sha1_number;
break;
case 'h':
case '?':
usage();

13
pwgen.h
View File

@ -26,14 +26,11 @@ struct pw_element {
#define PW_ONE_NUMBER 0x0001
#define PW_ONE_CASE 0x0002
struct pwgen_func {
const char *name;
void (*func)(char *buf, int size, int pw_flags);
int flags;
};
/* Function prototypes */
/* pointer to choose between random or sha1 pseudo random number generator */
extern int (*pw_number)(int max_num);
/* pw_phonemes.c */
extern void pw_phonemes(char *buf, int size, int pw_flags);
@ -42,3 +39,7 @@ extern void pw_rand(char *buf, int size, int pw_flags);
/* randnum.c */
extern int pw_random_number(int max_num);
/* sha1num.c */
extern void pw_sha1_init(char *sha1);
extern int pw_sha1_number(int max_num);

View File

@ -21,8 +21,10 @@
extern double drand48(void);
#endif
static int get_random_fd(void);
/* Borrowed/adapted from e2fsprogs's UUID generation code */
static int get_random_fd(void)
static int get_random_fd()
{
struct timeval tv;
static int fd = -2;
@ -56,7 +58,8 @@ static int get_random_fd(void)
* Generate a random number n, where 0 <= n < max_num, using
* /dev/urandom if possible.
*/
int pw_random_number(int max_num)
int pw_random_number(max_num)
int max_num;
{
int i, fd = get_random_fd();
int lose_counter = 0, nbytes=4;

399
sha1.c Normal file
View File

@ -0,0 +1,399 @@
/*
* FIPS-180-1 compliant SHA-1 implementation
*
* Copyright (C) 2001-2003 Christophe Devine
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include <string.h>
#include "sha1.h"
void sha1_process(sha1_context *ctx, uint8 data[64]);
#define GET_UINT32(n,b,i) \
{ \
(n) = ( (uint32) (b)[(i) ] << 24 ) \
| ( (uint32) (b)[(i) + 1] << 16 ) \
| ( (uint32) (b)[(i) + 2] << 8 ) \
| ( (uint32) (b)[(i) + 3] ); \
}
#define PUT_UINT32(n,b,i) \
{ \
(b)[(i) ] = (uint8) ( (n) >> 24 ); \
(b)[(i) + 1] = (uint8) ( (n) >> 16 ); \
(b)[(i) + 2] = (uint8) ( (n) >> 8 ); \
(b)[(i) + 3] = (uint8) ( (n) ); \
}
void sha1_starts(ctx)
sha1_context *ctx;
{
ctx->total[0] = 0;
ctx->total[1] = 0;
ctx->state[0] = 0x67452301;
ctx->state[1] = 0xEFCDAB89;
ctx->state[2] = 0x98BADCFE;
ctx->state[3] = 0x10325476;
ctx->state[4] = 0xC3D2E1F0;
}
void sha1_process(ctx, data)
sha1_context *ctx;
uint8 data[64];
{
uint32 temp, W[16], A, B, C, D, E;
GET_UINT32( W[0], data, 0 );
GET_UINT32( W[1], data, 4 );
GET_UINT32( W[2], data, 8 );
GET_UINT32( W[3], data, 12 );
GET_UINT32( W[4], data, 16 );
GET_UINT32( W[5], data, 20 );
GET_UINT32( W[6], data, 24 );
GET_UINT32( W[7], data, 28 );
GET_UINT32( W[8], data, 32 );
GET_UINT32( W[9], data, 36 );
GET_UINT32( W[10], data, 40 );
GET_UINT32( W[11], data, 44 );
GET_UINT32( W[12], data, 48 );
GET_UINT32( W[13], data, 52 );
GET_UINT32( W[14], data, 56 );
GET_UINT32( W[15], data, 60 );
#define S(x,n) ((x << n) | ((x & 0xFFFFFFFF) >> (32 - n)))
#define R(t) \
( \
temp = W[(t - 3) & 0x0F] ^ W[(t - 8) & 0x0F] ^ \
W[(t - 14) & 0x0F] ^ W[ t & 0x0F], \
( W[t & 0x0F] = S(temp,1) ) \
)
#define P(a,b,c,d,e,x) \
{ \
e += S(a,5) + F(b,c,d) + K + x; b = S(b,30); \
}
A = ctx->state[0];
B = ctx->state[1];
C = ctx->state[2];
D = ctx->state[3];
E = ctx->state[4];
#define F(x,y,z) (z ^ (x & (y ^ z)))
#define K 0x5A827999
P( A, B, C, D, E, W[0] );
P( E, A, B, C, D, W[1] );
P( D, E, A, B, C, W[2] );
P( C, D, E, A, B, W[3] );
P( B, C, D, E, A, W[4] );
P( A, B, C, D, E, W[5] );
P( E, A, B, C, D, W[6] );
P( D, E, A, B, C, W[7] );
P( C, D, E, A, B, W[8] );
P( B, C, D, E, A, W[9] );
P( A, B, C, D, E, W[10] );
P( E, A, B, C, D, W[11] );
P( D, E, A, B, C, W[12] );
P( C, D, E, A, B, W[13] );
P( B, C, D, E, A, W[14] );
P( A, B, C, D, E, W[15] );
P( E, A, B, C, D, R(16) );
P( D, E, A, B, C, R(17) );
P( C, D, E, A, B, R(18) );
P( B, C, D, E, A, R(19) );
#undef K
#undef F
#define F(x,y,z) (x ^ y ^ z)
#define K 0x6ED9EBA1
P( A, B, C, D, E, R(20) );
P( E, A, B, C, D, R(21) );
P( D, E, A, B, C, R(22) );
P( C, D, E, A, B, R(23) );
P( B, C, D, E, A, R(24) );
P( A, B, C, D, E, R(25) );
P( E, A, B, C, D, R(26) );
P( D, E, A, B, C, R(27) );
P( C, D, E, A, B, R(28) );
P( B, C, D, E, A, R(29) );
P( A, B, C, D, E, R(30) );
P( E, A, B, C, D, R(31) );
P( D, E, A, B, C, R(32) );
P( C, D, E, A, B, R(33) );
P( B, C, D, E, A, R(34) );
P( A, B, C, D, E, R(35) );
P( E, A, B, C, D, R(36) );
P( D, E, A, B, C, R(37) );
P( C, D, E, A, B, R(38) );
P( B, C, D, E, A, R(39) );
#undef K
#undef F
#define F(x,y,z) ((x & y) | (z & (x | y)))
#define K 0x8F1BBCDC
P( A, B, C, D, E, R(40) );
P( E, A, B, C, D, R(41) );
P( D, E, A, B, C, R(42) );
P( C, D, E, A, B, R(43) );
P( B, C, D, E, A, R(44) );
P( A, B, C, D, E, R(45) );
P( E, A, B, C, D, R(46) );
P( D, E, A, B, C, R(47) );
P( C, D, E, A, B, R(48) );
P( B, C, D, E, A, R(49) );
P( A, B, C, D, E, R(50) );
P( E, A, B, C, D, R(51) );
P( D, E, A, B, C, R(52) );
P( C, D, E, A, B, R(53) );
P( B, C, D, E, A, R(54) );
P( A, B, C, D, E, R(55) );
P( E, A, B, C, D, R(56) );
P( D, E, A, B, C, R(57) );
P( C, D, E, A, B, R(58) );
P( B, C, D, E, A, R(59) );
#undef K
#undef F
#define F(x,y,z) (x ^ y ^ z)
#define K 0xCA62C1D6
P( A, B, C, D, E, R(60) );
P( E, A, B, C, D, R(61) );
P( D, E, A, B, C, R(62) );
P( C, D, E, A, B, R(63) );
P( B, C, D, E, A, R(64) );
P( A, B, C, D, E, R(65) );
P( E, A, B, C, D, R(66) );
P( D, E, A, B, C, R(67) );
P( C, D, E, A, B, R(68) );
P( B, C, D, E, A, R(69) );
P( A, B, C, D, E, R(70) );
P( E, A, B, C, D, R(71) );
P( D, E, A, B, C, R(72) );
P( C, D, E, A, B, R(73) );
P( B, C, D, E, A, R(74) );
P( A, B, C, D, E, R(75) );
P( E, A, B, C, D, R(76) );
P( D, E, A, B, C, R(77) );
P( C, D, E, A, B, R(78) );
P( B, C, D, E, A, R(79) );
#undef K
#undef F
ctx->state[0] += A;
ctx->state[1] += B;
ctx->state[2] += C;
ctx->state[3] += D;
ctx->state[4] += E;
}
void sha1_update(ctx, input, length )
sha1_context *ctx;
uint8 *input;
uint32 length;
{
uint32 left, fill;
if( ! length ) return;
left = ctx->total[0] & 0x3F;
fill = 64 - left;
ctx->total[0] += length;
ctx->total[0] &= 0xFFFFFFFF;
if( ctx->total[0] < length )
ctx->total[1]++;
if( left && length >= fill )
{
memcpy( (void *) (ctx->buffer + left),
(void *) input, fill );
sha1_process( ctx, ctx->buffer );
length -= fill;
input += fill;
left = 0;
}
while( length >= 64 )
{
sha1_process( ctx, input );
length -= 64;
input += 64;
}
if( length )
{
memcpy( (void *) (ctx->buffer + left),
(void *) input, length );
}
}
static uint8 sha1_padding[64] =
{
0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
};
void sha1_finish( ctx, digest )
sha1_context *ctx;
uint8 digest[20];
{
uint32 last, padn;
uint32 high, low;
uint8 msglen[8];
high = ( ctx->total[0] >> 29 )
| ( ctx->total[1] << 3 );
low = ( ctx->total[0] << 3 );
PUT_UINT32( high, msglen, 0 );
PUT_UINT32( low, msglen, 4 );
last = ctx->total[0] & 0x3F;
padn = ( last < 56 ) ? ( 56 - last ) : ( 120 - last );
sha1_update( ctx, sha1_padding, padn );
sha1_update( ctx, msglen, 8 );
PUT_UINT32( ctx->state[0], digest, 0 );
PUT_UINT32( ctx->state[1], digest, 4 );
PUT_UINT32( ctx->state[2], digest, 8 );
PUT_UINT32( ctx->state[3], digest, 12 );
PUT_UINT32( ctx->state[4], digest, 16 );
}
#ifdef TEST
#include <stdlib.h>
#include <stdio.h>
/*
* those are the standard FIPS-180-1 test vectors
*/
static char *msg[] =
{
"abc",
"abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq",
NULL
};
static char *val[] =
{
"a9993e364706816aba3e25717850c26c9cd0d89d",
"84983e441c3bd26ebaae4aa1f95129e5e54670f1",
"34aa973cd4c4daa4f61eeb2bdbad27316534016f"
};
int main( argc, argv )
int argc;
char **argv;
{
FILE *f;
int i, j;
char output[41];
sha1_context ctx;
unsigned char buf[1000];
unsigned char sha1sum[20];
if( argc < 2 )
{
printf( "\n SHA-1 Validation Tests:\n\n" );
for( i = 0; i < 3; i++ )
{
printf( " Test %d ", i + 1 );
sha1_starts( &ctx );
if( i < 2 )
{
sha1_update( &ctx, (uint8 *) msg[i],
strlen( msg[i] ) );
}
else
{
memset( buf, 'a', 1000 );
for( j = 0; j < 1000; j++ )
{
sha1_update( &ctx, (uint8 *) buf, 1000 );
}
}
sha1_finish( &ctx, sha1sum );
for( j = 0; j < 20; j++ )
{
sprintf( output + j * 2, "%02x", sha1sum[j] );
}
if( memcmp( output, val[i], 40 ) )
{
printf( "failed!\n" );
return( 1 );
}
printf( "passed.\n" );
}
printf( "\n" );
}
else
{
if( ! ( f = fopen( argv[1], "rb" ) ) )
{
perror( "fopen" );
return( 1 );
}
sha1_starts( &ctx );
while( ( i = fread( buf, 1, sizeof( buf ), f ) ) > 0 )
{
sha1_update( &ctx, buf, i );
}
sha1_finish( &ctx, sha1sum );
for( j = 0; j < 20; j++ )
{
printf( "%02x", sha1sum[j] );
}
printf( " %s\n", argv[1] );
}
return( 0 );
}
#endif

24
sha1.h Normal file
View File

@ -0,0 +1,24 @@
#ifndef _SHA1_H
#define _SHA1_H
#ifndef uint8
#define uint8 unsigned char
#endif
#ifndef uint32
#define uint32 unsigned long int
#endif
typedef struct
{
uint32 total[2];
uint32 state[5];
uint8 buffer[64];
}
sha1_context;
void sha1_starts( sha1_context *ctx );
void sha1_update( sha1_context *ctx, uint8 *input, uint32 length );
void sha1_finish( sha1_context *ctx, uint8 digest[20] );
#endif /* sha1.h */

77
sha1num.c Normal file
View File

@ -0,0 +1,77 @@
/*
* sha1num.c --- generate sha1 hash based, pseudo random numbers
*
* Copyright (C) 2005 by Olivier Guerrier
*
* This file may be distributed under the terms of the GNU Public
* License.
*/
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include "pwgen.h"
#include "sha1.h"
sha1_context sha1_ctx;
char *sha1_seed;
const char *sha1_magic="pwgen";
unsigned char sha1sum[20];
int sha1sum_idx=20;
void pw_sha1_init(sha1)
char *sha1;
{
int i = 0;
char *seed;
FILE *f;
unsigned char buf[1024];
if ((seed = strchr(sha1,'#'))) {
*(seed++) = 0;
sha1_seed = malloc(strlen(seed)+1);
if (!sha1_seed) {
fprintf(stderr, "Couldn't malloc sha1_seed buffer.\n");
exit(1);
}
strcpy(sha1_seed, seed);
}
else {
sha1_seed = malloc(strlen(sha1_magic)+1);
if (!sha1_seed) {
fprintf(stderr, "Couldn't malloc sha1_seed buffer.\n");
exit(1);
}
strcpy(sha1_seed, sha1_magic);
}
if( ! ( f = fopen( sha1, "rb" ) ) ) {
fprintf(stderr, "Couldn't open file: %s.\n", sha1);
exit(1);
}
sha1_starts( &sha1_ctx );
while( ( i = fread( buf, 1, sizeof( buf ), f ) ) > 0 ) {
sha1_update( &sha1_ctx, buf, i );
}
return;
}
int pw_sha1_number(max_num)
int max_num;
{
int val;
sha1_context ctx;
if(sha1sum_idx>19)
{
sha1sum_idx = 0;
sha1_update(&sha1_ctx, sha1_seed, strlen(sha1_seed));
ctx = sha1_ctx;
sha1_finish(&ctx, sha1sum );
}
val = (int) (sha1sum[sha1sum_idx++] / ((float) 256) * max_num);
return (val);
}