#!/usr/bin/perl
# Copyright (C) 2008, Nigel Kukard  <nkukard@lbsd.net>
#
# Author: Nigel Kukard  <nkukard@lbsd.net>
# 
# 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.,
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.


use strict;
use warnings;

use Getopt::Long;


my $VERSION = "0.0.2";



print(STDERR "nkmkpass v$VERSION - Copyright (c) 2008, Nigel Kukard  <nkukard\@lbsd.net>\n\n");

# Grab options
my %optctl = ();
GetOptions(\%optctl, 
	"strength=s",
	"length=n",
	"type=s",
	"passphrase-words=s",
	"passphrase-wordlen=s",
	"help") or exit 1;

# Check for help
if (defined($optctl{'help'})) {
	displayHelp();
	exit 0;
}


# Check strength
if (defined($optctl{'strength'})) {
	# Make sure its valid
	if ($optctl{'strength'} ne "easy" && $optctl{'strength'} ne "hard" && $optctl{'strength'} ne "vhard") {	
		print(STDERR "Strength is invalid, valid values are: easy hard vhard\n");
		displayHelp();
		exit 1;
	}
} else {
	$optctl{'strength'} = "vhard";
}
# Check length
if (defined($optctl{'length'})) {
	# Make sure its valid
	if (!$optctl{'length'} > 3) {	
		print(STDERR "Length is invalid, valid values are: > 3\n");
		displayHelp();
		exit 1;
	}
} else {
	$optctl{'length'} = 10;
}
# Check type
if (defined($optctl{'type'})) {
	# Make sure its valid
	if ($optctl{'type'} ne "password" && $optctl{'type'} ne "passphrase") {	
		print(STDERR "Type is invalid, valid values are: password passphrase\n");
		displayHelp();
		exit 1;
	}
} else {
	$optctl{'type'} = "password";
}
# Check passphrase-words
if (defined($optctl{'passphrase-words'})) {
	# Make sure its valid
	if ($optctl{'passphrase-words'} =~ /^([0-9]+)(?::([0-9]+))?$/) {
		my ($min,$max) = ($1,$2);

		$max = $min if (!defined($max));

		# Check params
		if (!defined($min) || $min < 1 || $max < $min) {
			print(STDERR "Passphrase words is invalid, valid values are: [n:y] where n > 1 and y > n\n");
			displayHelp();
			exit 1;
		}
		
		$optctl{'passphrase-words-min'} = $min;
		$optctl{'passphrase-words-max'} = $max;
	} else {
		print(STDERR "Passphrase words is invalid, valid values are: [n:y] where n > 1 and y > n\n");
		displayHelp();
		exit 1;
	}
} else {
	$optctl{'passphrase-words-min'} = 3; 
	$optctl{'passphrase-words-max'} = 8;
}
# Check passphrase-wordlen
if (defined($optctl{'passphrase-wordlen'})) {
	# Make sure its valid
	if ($optctl{'passphrase-wordlen'} =~ /^([0-9]+)(?::([0-9]+))?$/) {
		my ($min,$max) = ($1,$2);
		
		$max = $min if (!defined($max));

		# Check params
		if ($min < 2 || $max < $min) {
			print(STDERR "Passphrase wordlen is invalid, valid values are: [n:y] where n > 2 and y > n\n");
			displayHelp();
			exit 1;
		}
		
		$optctl{'passphrase-wordlen-min'} = $min;
		$optctl{'passphrase-wordlen-max'} = $max;
	} else {
		print(STDERR "Passphrase wordlen is invalid, valid values are: [n:y] where n > 2 and y > n\n");
		displayHelp();
		exit 1;
	}
} else {
	$optctl{'passphrase-wordlen-min'} = 2;
	$optctl{'passphrase-wordlen-max'} = 8;
}



# Its a password we want to generate
if ($optctl{'type'} eq "password") {
	print "Result: " . generateString($optctl{'strength'},$optctl{'length'}) . "\n";

# Its a passphrase
} elsif ($optctl{'type'} eq "passphrase") {
	my $words = $optctl{'passphrase-words-min'} + int(rand($optctl{'passphrase-words-max'} - $optctl{'passphrase-words-min'}));

	my $string = "";

	# Loop
	for (my $i = 0; $i < $words; $i++) {
		# Get word length for this word
		my $wordlen = $optctl{'passphrase-wordlen-min'} + int(rand($optctl{'passphrase-wordlen-max'} - $optctl{'passphrase-wordlen-min'}));

		$string .= generateString($optctl{'strength'},$wordlen) . " ";
	}

	print "Result: $string\n";
}



# Function to generate a random string
sub generateString 
{
	my ($strength,$length) = @_;


	# Basic chars
	my $chars = 'abcdefghijkmnpqrstuvwxyz23456789';
	
	# Check how hard this string must be
	$chars .= 'ABCDEFGHJKLMNPQRSTUVWXYZ' if ($strength eq "hard" || $strength eq "vhard");
	$chars .= '~!@#$%^&*()_-+={}[]:;|\\<>/?' if ($strength eq "vhard");

	my $str = "";

	# Generate
	while (length($str) < $length) {
		$str .= substr($chars, (int(rand(length($chars)))), 1);
	}
	
	return $str;
}


# Display help
sub displayHelp {
	print(STDERR<<EOF);
Usage: $0 [args]
    --strength=<easy|hard|vhard>         Strength of generated string (vhard)
    --length=<length>                    Length of generated string (10)
    --type=<password|passphrase>         Type of string to generate
    --passphrase-words=<min:max|num>     Number of words to generate (4)
    --passphrase-wordlen=<min:max|num>   Word length (3:5)

EOF
}


