Friday, April 15, 2011

Generating Strong Passwords

I'm working on a project that requires several passwords that will be used only occasionally but are not “throw away” passwords. I also need them to be reasonably strong. I could just make some up and save them in an encrypted file, of course, but that would mean just one more file to update and maintain. What I really want is some way to generate then on the fly whenever I need them. The other day I saw a password generator, SHA1_Pass, that does pretty much what I need. What I don't need, though, is a heavy duty app with gui dialog boxes and all that.

So in the DIY/NIH spirit I rolled my own. I want to just type in a name such as “Joe” or “Mary” and get back a strong password. A simple way of doing that is to hash the name with SHA1 and map the result into printable characters. For a little extra security, I concatenated some random characters to the name before I hashed it. Here's the resulting c code:

 1:  /*
 2:   * makepw.c -- generate strong passwords given a key phrase
 3:   *
 4:   * Compile with:
 5:   *    gcc -Wall -lcrypto -o makepw makepw.c
 6:   *
 7:   */
 9:  #include <openssl/sha.h>
10:  #include <stdio.h>
11:  #include <stdlib.h>
12:  #include <string.h>
13:  #include <stdint.h>
15:  int main (int argc, char **argv)
16:  {
17:      int i;
18:      int j;
19:      uint32_t wd;
20:      char xlat[] = "abcdefghijkmnopqrstuvwxyzABCDEFGHIJKLM"
21:          "NOPQRSTUVWXYZ023456789.!-$";
22:      char buf[ 256 ] = "Some-Secret-Key";
23:      union
24:      {
25:          uint32_t s1[ 5 ];
26:          unsigned char md[ 20 ];
27:      } u;
29:      if ( argc != 2 )
30:      {
31:          puts( "wrong number of arguments" );
32:          exit( 1 );
33:      }
34:      strcat( buf, argv[ 1 ] );
35:      SHA1( (unsigned char *)buf, strlen( buf ), );
36:      for ( i = 0; i < 2; i++ )
37:      {
38:          wd = u.s1[ i ];
39:          for ( j = 0; j < 5; j++ )
40:          {
41:              putchar( xlat[ wd & 0x3f ] );
42:              wd >>= 6;
43:          }
44:      }
45:      exit( 0 );
46:  }

As you can see, the code is straightforward. The random characters are already sitting in buf so I just concatenate the input into buf and call SHA1 to hash the result (lines 34–35). In lines 36–44, I translate the result into printable characters 6 bits at a time using xlat and output each character as it's produced. The upper 2 bits of each word are discarded. The astute reader will notice that the union introduces an endian issue so if you are going to run this on machines with different architectures you will have to compensate for that. An easy way is to replace line 38 with

wd = htonl([ i ] );

Also notice that there's a buffer overflow vulnerability because the size of the input isn't checked but this is just a quick hack for my own use so I kept it simple.

If you need more (or less) characters in the password, just adjust the i for loop end count—you get 5 characters for each time through the loop.

When I run makepw with input jcs I get


This works well for me but if you want to do something like this you may want to replace the Some-Secret-Key in line 22 with a key that you enter instead. Then you would call it as

makepw secret-key jcs

That provides a more secure application at the expense of having to remember and enter a master key.

On the other hand, you could make it more like SHA1_Pass and not worry about a secret key at all. Instead of entering just a name like “Joe” or “Yahoo!” you would enter some random phrase like “My sister Mary's password for Yahoo!”.

We can refine this a little further. One nice thing about SHA1_Pass is that it puts the password into the clip board for you so that all you have to do is paste it into wherever you need it. I'm too lazy to figure out how to do that programmatically and since this is running on a Mac I'd probably have to use Objective-C. Fortunately, there's an easier way. I just wrote a tiny script that pipes the password into pbcopy, an Apple utility that copies its input to the paste buffer. X users can use the xclip utility instead.

#! /bin/bash
makepw $1 | pbcopy

Now just name this script make-pw, make it executable, and put it in your execution path and you're good to go. If you're a Mac user, you can use Automator to turn it into a service but if you're going to do that you might as well use (the free) SHA1_Pass.

A final note: The above code was run as a Babel code block and directly generated the resulting password. The #+begin_src line is

#+begin_src c -n :flags -lcrypto :cmdline jcs :exports both

The more I use Babel, the more I like it.

No comments:

Post a Comment