Friday, December 23, 2011

Generate Secure Passwords with Linux

One of the easiest ways to increase your security while browsing the net is to review the quality of your passwords.  It's not difficult, and it doesn't take long. 

After doing the obvious things to increase security, such as using firewalls and internet security packages, you may want to look at your passwords.  There are two big no no's when it comes to passwords, using simple ones, and using the same one for everything.  I'll bet some of you are doing one of these things, and who can blame you.  I have maybe 30 to 40 different logins and remembering unique, complex passwords for all of them is near impossible.  Even if you can remember them, you may mix up passwords between accounts and get shut out after three attempts.  So it becomes easy to use one simple password for everything, which leaves your logins vulnerable.  Whereas using secure passwords and securing a password list for all these accounts is something you can control and is manageable.  (How to do this elegantly is an ongoing longterm project I am working on)

The reason for having complex passwords is pretty obvious.  If an attacker is trying to break a password by guessing, it's going to take a much longer to guess a password that's 15 characters and has punctuation symbols in it, than guessing an 8 character password with only letters.  This task is done by software and can be done rather quickly in some cases.  There are other reasons for having a complex password relating to how they are stored in hashes.  Short passwords using a small symbol set can be recovered from their hash using pre-compiled rainbow tables.

Now let's say that you have chosen a completely random password that no-one would ever guess and you decide to use it for everything.  If one of the sites that you use this password on were to have poor security and your password was discovered, the attacker has access to all your accounts.  Not a good thing.

Some people have schemes like replacing the letters with other symbols, such as "MY 5E(RET P@55W0RD", these are safer but still not secure.  There is a pretty good chance whatever secret scheme you come up with has been thought of by someone before.  There's some good information on Wikipedia about passwords. en.wikipedia.org/wiki/Password_strength & en.wikipedia.org/wiki/Password are worth a read.

Ideally a password should be as long as possible and made up of randomly selected symbols from an allowed set.  In most cases the allowed set will be made up of a subset of the characters that appear on your keyboard, and randomly selecting them can be a hard task.  For this reason I put together a python script that would generate random passwords for me.  It's a bit hacky but gets the job done.  For a bit of fun I thought I would make it Unicode compatible too.

#! /usr/bin/env python
# -*- coding: utf-8 -*-

import argparse
import struct
import sys
import codecs
import math

#Custom print function to encode output in UTF-8
def UTFprint(in_string):
    in_string = in_string.encode('utf-8')
    sys.stdout.write(in_string)
    sys.stdout.write("\n")
    return

#Create command line parser
parser = argparse.ArgumentParser(description='Generate a password')
parser.add_argument('length', type=int, help='number of symbols in the password')
parser.add_argument('groups', type=str, help='groupcodes l=lower case letters, L=upper case letters, n=numbers, s=space, h=lower case hex, H=upper case hex, g=lower case greek, G=upper case greek, p=punctuation symbols, r=runes, e=high ASCII, k=keyboard symbols, c=CP437')
parser.add_argument('extras', type=str, help='extra symbols')
parser.add_argument('separate', type=str, help='separator string')
parser.add_argument('sepspace', type=int, help='number of symbols between separators')
parser.add_argument('-v', '--verbose', default=0, action='store_const', const=1, help='verbose output')
parser.add_argument('-r', '--random', default=0, action='store_const', const=1, help='when selected uses dev/random instead of dev/urandom')
args = parser.parse_args()

#define symbol strings
L_str = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' #upper case letters
l_str = 'abcdefghijklmnopqrstuvwxyz' #lower case letters
n_str = '0123456789' #numbers
s_str = ' ' #space
H_str = '0123456789ABCDEF' #upper case hex
h_str = '0123456789abcdef' #lower case hex
g_str = 'αβγδεζηθικλμνξοπρστυφχψω'.decode('utf8') #lower case greek
G_str = 'ΑΒΓΔΕΖΗΘΙΚΛΜΝΞΟΠΡΣΤΥΦΧΨΩ'.decode('utf8') #upper case greek
p_str = '!"#$%\'&()*+,-./;:<=>?@[\]^`_{}|~' #punctuation symbols
r_str = 'ᚠᚡᚢᚣᚤᚥᚦᚨᚩᚪᚫᚬᚭᚮᚯᚰᚱᚲᚳᚴᚵᚶᚷᚸᚹᚺᚻᚼᚽᚾᚿᛀᛁᛂᛃᛄᛅᛆᛇᛈᛉᛊᛋᛌᛍᛎᛏᛐᛑᛒᛓᛔᛕᛖᛗᛘᛙᛚᛛᛜᛝᛞᛟᛠᛡᛢᛣᛤᛥᛦᛧᛨᛩᛪᛮᛯᛰ'.decode('utf8') #runes
e_str = '☺☻♥♦♣♠•◘○◙♂♀♪♫☼►◄↕‼¶§▬↨↑↓→←∟↔▲▼⌂ÇüéâäàåçêëèïîìÄÅÉæÆôöòûùÿÖÜ¢£¥₧ƒáíóúñѪº¿⌐¬½¼¡«»░▒▓│┤╡╢╖╕╣║╗╝╜╛┐└┴┬├─┼╞╟╚╔╩╦╠═╬╧╨╤╥╙╘╒╓╫╪┛┌█▄▌▐▀αßΓπΣσµτΦΘΩδ∞φε∩≡±≥≤⌠⌡÷≈°∙·√ⁿ²■  '.decode('utf8') #high ASCII
k_str = l_str + L_str + s_str + n_str + p_str #keyboard symbols
c_str = e_str + k_str #CP437

#Assemble character list from command line options
sym_list = ''
if args.groups.count('L') > 0:
    sym_list = sym_list + L_str
if args.groups.count('l') > 0:
    sym_list = sym_list + l_str
if args.groups.count('s') > 0:
    sym_list = sym_list + s_str
if args.groups.count('n') > 0:
    sym_list = sym_list + n_str
if args.groups.count('H') > 0:
    sym_list = sym_list + H_str
if args.groups.count('h') > 0:
    sym_list = sym_list + h_str
if args.groups.count('g') > 0:
    sym_list = sym_list + g_str
if args.groups.count('G') > 0:
    sym_list = sym_list + G_str
if args.groups.count('p') > 0:
    sym_list = sym_list + p_str
if args.groups.count('r') > 0:
    sym_list = sym_list + r_str
if args.groups.count('k') > 0:
    sym_list = sym_list + k_str
if args.groups.count('e') > 0:
    sym_list = sym_list + e_str
if args.groups.count('c') > 0:
    sym_list = sym_list + c_str


#Add extra symbols to the symbol list
sym_list = sym_list + args.extras.decode('utf8')

#Sort list and remove duplicate symbols
sym_list = ''.join(sorted(set(sym_list)))

#If the space between separator strings is 0,
#set the separator to null
separator = args.separate.decode('utf8')
if args.sepspace == 0:
    separator = ''

#Set parameter variables
num_syms = len(sym_list)
pass_length = args.length
bits_per_symbol = int(math.ceil(math.log(num_syms,2)))

#Declare output variable
password = ""

#Set loop counters
symbols_gathered = 0
separator_count = 0

#/dev/random warning
UTFprint("")
if args.random == 1:
    UTFprint("/dev/random has been selected and may take")
    UTFprint("some time. To decrease the generation time")
    UTFprint("type some random data in another prompt.")
    UTFprint("")


#Read characters from /dev/urandom and if they are suitable
#use them to add symbols to the password
if args.random == 1:
    f = open("/dev/random","rb")
else:
    f = open("/dev/urandom","rb")

while (symbols_gathered < pass_length):
    rnd_str1 = f.read(1)
    rnd_str2 = f.read(1)
    rand_int1 = struct.unpack('B', rnd_str1)[0]
    rand_int2 = struct.unpack('B', rnd_str2)[0]
    rand_int = rand_int1 * 256 + rand_int2
    rand_int = rand_int % pow(2, bits_per_symbol)
    if (rand_int < num_syms):
        if (separator_count == args.sepspace):
            password = password + separator
            separator_count = 0
        password = password + sym_list[rand_int]
        symbols_gathered = symbols_gathered + 1
        separator_count = separator_count + 1
f.close()

#Output the results
if args.verbose == 1:
#    if args.random == 1:
#        UTFprint("/dev/random has been selected and may take")
#        UTFprint("some time. To decrease the generation time")
#        UTFprint("type some random data in another prompt.")
    UTFprint("Symbol Space:           " + str(num_syms))
    UTFprint("Password Length:        " + str(pass_length))
    UTFprint("Symbol List:            " + "<" + sym_list + ">")
    UTFprint("Separator String:       " + "<" + separator + ">")
    UTFprint("Password:               <" + password + ">")
    UTFprint("")
else:
    UTFprint(password)


Pretty basic.  It allows me to do things like use different sets of characters and add separators to the passwords as well.  Below are a few basic usage examples.

pass_gen.py examples

The syntax is pretty easy: number of characters to generate, symbol sets, extra characters, separator characters, characters between separators, and options -v for verbose and -r for /dev/random instead of /dev/urandom.

Example 1 is 16 symbols long from a hexadecimal character set, a colon every 4 symbols, with /dev/random and verbose
Example 2 is 16 symbols long using numbers, with tick and cross as extra symbols, a circle separator every 4 symbols, and verbose
Example 3 is 16 keyboard symbols with  no separators
Example 4 is 30 rune symbols with no separators
Example 5 is 30 small Greek letters with no separators

Here is a link to the file. pass_gen.py  With all the quirks of different shells and flavours of linux the unicode stuff might not work too well, most cases it should be a matter of tweaking things.

After I wrote the script I saw a simple command on Hak5 that can generate a password.  It's functionality covers pretty much any password you would need to create, and is so much simpler.

/dev/urandom | tr -dc A-Za-z0-9_ | head -c8 ; echo

 With a bit of modification I came up with this that generates multiple passwords.

cat /dev/urandom | tr -dc '[a-z][A-Z][0-9]-_!@#$%^&*()_+{}|:?=' | fold -w 10| head -n 5

4uJM0FV2Bc
Jx!CA0q3r3
sI@rR0PiC1
SPVJ{+d$b_
MUWm@&+Uuv

See how easy it is to create strong passwords.  This is of course no guarantee that you wont have one of your accounts compromised in the future, but it's a simple step that will make your account more secure and less attractive to hackers.

Wednesday, December 14, 2011

Converting Image Files to PDFs

Recently I have been processing some image files and needed a way to combine them into a PDF file.  Image files by themselves can get a little bit messy, so it just tidies thing up a little by collating related images.  Don't get me wrong, I still hang on to the originals but PDFs are nicer to actually use.

So here is the situation, I have a folder that contains all the images named sequentially.  The images then need to be converted to PDFs and then combined into one file.  Additionally I want a grayscale version and a colour version.  As usual, the easiest way to do this is with a script in linux.  If you haven't got them already, you will need to run the following commands to install packages for this to work.

sudo apt-get install imagemagick
sudo apt-get install pdftk

Imagemagick is an awesome tool for processing images, particularly when processing batches of them, and Pdftk is a tool kit that allows you to rearrange, remove, and add pages to to pdf files.  With these two tools the following script does the job nicely.

#!/bin/bash

mkdir Col
mkdir BW
rm -rf ./Col/*
rm -rf ./BW/*

ls *.png | sed -e "s/.png$//" | xargs -r -I FILE \
convert FILE.png -density 300x300 -compress jpeg \
-quality 60 ./Col/FILE.pdf
 
ls *.png | sed -e "s/.png$//" | xargs -r -I FILE \
convert FILE.png -colorspace gray -density 300x300 \
-compress jpeg -quality 60 ./BW/FILE.pdf

pdftk ./BW/*.pdf cat output BW.pdf
pdftk ./Col/*.pdf cat output Col.pdf

rm -rf ./Col/*
rm -rf ./BW/*

rmdir ./Col
rmdir ./BW

The script is run from the directory that contains the images.  It first creates two directories, one for the colour PDFs and one for the GrayScale PDFs.  Any files that may exist in these directories are deleted.  Next the images are converted to PDFs by the ImageMagick convert command.  All the png files from the directory are found via the ls command and piped to sed where the file extension is removed.  Xargs is then used to run the convert command.  Options are used to control the output, density sets the viewing DPI, compress set the compression method, and quality set the JPEG compression quality.  Colorspace is used in the second command to set the output to grayscale.

Next pdftk is used to combine the PDF's that were just created into one file.  The order of the pages in the PDF is based on the alphabetical order of the input files.

The intermediary files and folders are then deleted.  Job done, and I could leave the computer unattended for most of the time as well.

Sunday, December 4, 2011

Benefits of PNG Files

Recently while scanning some old paper work, I spotted something that beautifully illustrates one of the properties of PNG files.  After scanning a batch of images and converting them to PNG format I then ordered them by size in Windows Explorer.  A clear pattern immediately became visible by looking at the preview icons.  A cut down version of this is shown below.

File Preview Icons

A PNG file is simply an image file that uses loss less compression to reduce the file size, and the simpler the image is, the better the compression algorithm works.  By looking at the image above you can see that as the files become smaller they become simpler in structure, ie less variations in colour and less detail.  From this group of images the best compression ratio obtained was about 3 to 1, but much higher compression ratios can be obtained.

The map image below from Open Street Map for example has a compression ratio of about 20 to 1.  It is made up of only a handful of colours and has large patches of continuous colour, making it a perfect candidate for PNG compression.


Highly Compressed PNG File
This property was useful in the OpenGL map viewer software that I wrote.  The main factor limiting the update rate of map tiles was the speed at which they could be read from the disk drive.  By reading the compressed files from the disk and decompressing them in memory a speed increase was obtained.

I have to admit that I quite like using PNG files.  When compared to a BMP file they win nearly all the time, you get the added feature of an alpha (transparency) channel, and the space saving via compression that they offer.  I would only use a BMP file if I needed something that was really easy to read.  So for archiving images, PNG is the way to go.

When comparing JPEG and PNG files it really depends on the image.  If the image is photo like and has lots of detail, then JPEG is the way to go if you don't mind a slight loss of quality through compression, otherwise PNG may be a good choice.