Due Date: 2/16/2010

advertisement
Project1: SecLogin: Securing authentication using case-based password hardening
CS 6238: Secure Computer Systems
Due Date: 2/16/2010
-
I.
Alan Carroll
Daniel Komaromy
Initialization Routine
During the initialization routine, we setup a configuration file that holds the parameters passed and can be
loaded in subsequent executions. This size of the history file is chosen during our program’s initialization
phase. This is important to satisfy the constant encrypted history file size; consequently, also the reason
the max password length is required, since (num. of distinguishing features == number of characters in
password).
[root@linbox cs6238]# ./main.py
$help
Printing options
init [--len_history|-l] len [--dir|-d] dir [--min|-m] min_len [--max|-M] max_len
initialize the system, with history_file size h(integer) and base directory d
(string: fully qualified path) and minimum password lengt min_len and maximum
password length max_len
load [--dir|-d] d: initialize the system by loading it from the config file,
found at the base directory d (string: fully qualified path)
add [--user|-u] username: add a new user with login name 'username' .
login [--user|-u] username: authenticate the user with login name 'username' .
exit: quit the program.
help: print this help menu.
$init -l 10 -d config -m 8 -M 20
Init a new system. Save config file.
SecLogin home created.
$
We are selecting a 160-bit prime number (q) during initialization and a random hpwd value to
statisty the Hpwd < q requirement.
def _generate_prime(self, len):
…
while(1):
rand_bytes = os.urandom(len / 8)
value = number.bytes_to_long(rand_bytes)
# Ensure high bit is set: multiply (=bitwise or) it by 2^(len-1)
value |= 2L ** (len-1)
count = 0
while (not number.isPrime(value)):
value = value + 2
count = count + 1
if (count == 1000):
break
if (number.isPrime(value)):
break
else:
pass
return value
This shows we are searching for an acceptable prime number and utilizing the number class from our
Crypto.Cipher module (from Crypto.Util import number). We are calculating hpwd: hpwd =
SUM(lgr_coeff_i * y_i) mod q using python’s map/reduce functional programming
def _compute_hpwd(self, lgr_coeffs, points):
sum_terms = map(lambda x,y: (x*y) % self.mod, lgr_coeffs, points)
return reduce(lambda x,y: (x+y) % self.mod, sum_terms)
We are selecting a random polynominal (f) of degree n-1 and then using this to create our instruction table
def _generate_polynomial(self, hpwd, degree):
coeffs = [self._generate_param(self.len) % self.mod for i in range(degree)]
coeffs.append(hpwd)
return numpy.poly1d(coeffs)
so that all our initial and values are valid. For this, we used the formula in the paper to complete this
creations. Finally, the history table is encrypted with Hpwd.
def _compute_instruction_table(self, pwd, y_pairs):
instruction_table_rows = []
for i in range(len(y_pairs)):
# i goes from 0 to m-1, but it is 1 to m according to paper, so +1
alpha = (y_pairs[i][0] + self._hash(pwd.lower(), 2*(i+1))) % self.mod
beta = (y_pairs[i][1] + self._hash(pwd.lower(), 2*(i+1) + 1)) %
self.mod
instruction_table_rows.append(tuple([alpha, beta]))
return instruction_table_rows
II.
Login
Our login routine first works by selecting our appropriate i or I from our instruction table (setup during
initialization) based on the feature values (i) for feature based on the case of the typed character.
insTable = InstructionTable.InstructionTable(pwd, path, False)
We decrypt the value from the instruction table using the pwd supplied and calculate the xi, yi coordinates
based on the i or I values selected from the instruction table using the formula given in the paper. Our
decryption and computation of coordinates is shown below.
def _decrypt(self, pwd, parameter):
#return number.bytes_to_long(parameter)
hashed_pwd = SHA256.new(pwd).digest()
#use 32 byte value as AES key
cryptObj = AES.new(hashed_pwd)
#decrypt parameter which is a bytestream ciphertext and turn it into a long
plaintext = cryptObj.decrypt(parameter)
pad_length = plaintext[len(plaintext) - 1]
if (ord(pad_length) == 0):
cutoff = len(plaintext) - cryptObj.block_size
else:
cutoff = len(plaintext) - ord(pad_length)
plaintext = plaintext[:cutoff]
return number.bytes_to_long(plaintext)
points = self._compute_points(pwd, insTable)
# _compute_points shown below
"""
Computes the points on the polynomial pol from pwd, alpha, beta.
Parameters:
pwd: the users password, used in G() and to determine which column to use
from the instruction table.
table: the instruction table used here to recover points: the actual
InstructionTable object
Note: in x calculation, we use i+1 instead of i because i value runs from 1 to
m, not 0 to m-1.
Every other usage of i is as index into the list (not the actual value), and
internally everything
is indexed from 0 to m-1.
Output: the list of computed (x,y) coordinates of the point on the polynomial
"""
def _compute_points(self, pwd, table):
points = []
#use as lowercase for encryption/decryption
pwd_encrypt = pwd.lower()
for i in range(len(pwd)):
if (self._get_case(pwd[i]) == 0):
x = 2*(i+1)
alpha = table.get_alpha(i, pwd_encrypt)
if (alpha == None):
utility.debug('Alpha recovery failed!')
return None
y = (alpha - self._hash(pwd_encrypt, 2*(i+1))) % self.mod
else:
x = (2*(i+1) + 1)
beta = table.get_beta(i, pwd_encrypt)
if (beta == None):
utility.debug('Beta recovery failed!')
return None
y = (beta - self._hash(pwd_encrypt, 2*(i+1) + 1)) % self.mod
points.append(tuple([x,y]))
return points
Once we calculate Hpwd using the retrieved coordinates using polynomial interpolation, we can use Hpwd
to decrypt our history file. Our history file, once decrypted, is in a native python double-ended queue for
speedy removal of the oldest feature values and addition to the newest feature value. This acts as a true
FIFO data structure.
hFile = HistoryFile.HistoryFile(len(pwd), self.history_length, path, hpwd)
# Creates a new AES object from the Crypto.Cipher module using the hpwd as the key for decrypting later on. This is stored
in-memory – never on disk.
self.cryptObj = AES.new((SHA256.new(number.long_to_bytes(hpwd))).digest())
…
# self.contents stores the decrypted history file contents in-memory. Decryption will fail if the improper key was given to
the AES (cryptObj) object.
self.contents = self.cryptObj.decrypt(content)
If decryption is successful, the Boolean value representing the login status is passed back to SecLogin.py
and the user is officially authenticated and welcomed to the system; however, the program will continue to
add a new feature vector for the current login in the history file and encrypt the file once again using hpwd.
SecLogin will: generate a new random polynomial -> new y pairs -> new alpha, beta -> and then update our
Instruction Table.
III.
Class Descriptions
Our system implements the following classes. Note, that each class implementation includes a unit
test that can be run to verify functionality. Alternatively, the main.py script can be executed to
instantiate the program with a command line interface (as shown in the walk through section of
this document).

HistoryFile - This class implements a History File object. It handles both the in memory
representation and the serialization/deserialization of the file. It is instantiated by SecLogin (see
below) each time a user interacts with the system (new user or user logging in). SecLogin calls its
deserialize() function to read the History File from the disk. This function determines whether the
supplied hpwd was correct or not, by testing the redundancy of the decrypted file. To provide this,
we attach its hash value to the serialized bytestream version of the file before it is ecnrypted and
dumped from memory. HistoryFile's serialize() function in turn writes out the file from memory,
update() updates it with a new feature vector, and get_feature_vector() returns the acceptable
case (lower/upper) for each position based on the current history file contents.

InstructionTable - This class implements an Instruction Table object. Similarly to HistoryFile, it
handles both the in memory representation and the serialization/deserialization of the file. The
same as HistoryFile, it is instantiated by SecLogin (see below) each time a user interacts with the
system (new user or user logging in). This class only handles alpha and beta values and takes care of
their encryption/decryption, lower lever functionality needed to get alpha, beta values from points
on the polynomial or vice versa is handled by SecLogin (see below). Its public functions are
serialize(), deserialize(), update_rows() and get_alpha(), get_beta(). The first three provide similar
functionality to the corresponding functions of HistoryFile (with the exception that update_rows()
completely replaces the contents of the table), while the latter two are used to query an instance of
the class for the alpha or beta value in a given row of the table. In order to get a correct plaintext
blocksize from the alpha, beta values that can be used with AES, we apply padding bytes to the
values once there are converted to a bytestream.

LoginServer - This class is one of the two example interface classes that we have created for our
system. It implements a socket server that listens for connection requests and handles them in a
multithreaded way. It can accept user creation and login attempts and returns a response to the
client that indicates success or failure.

MyOptionParser - This is a helper class, used to overwrite the default functionality of the optparse
module. In optparse, parsing errors are sent to stderr. This class traps them to allow execution
within our own command line interface, for example PromptInterface.

PromptInterface - This is the other interface class. It implements a command line interface to run
the hardened password authentication program. Its functionality can be seen by invoking the
command 'help'. Note, that some python interpreters do not handle the password input
functionality provided by the getpass module, in which case a warning message is displayed to
notify the user that typed passwords are echoed. IDLE on Linux is an example of such an
interpreter, while the default bash on Linux is an example that hides typed passwords properly.

SecLogin - This class drives the mathematical operations to create and recover the hardened
passwords, as described in the paper. It saves its configuration parameters into a file during
initialization, to make sure that it can be reloaded (i.e. user credentials do not get last after a
reboot). It's two public functions are new_user and login_user. The former creates a user, the later
validates its login. SecLogin handles Instruction Tables and History Files by calling the public
functions for the classes that implement them. It creates one instance of each class for each login
attempt, for example. SecLogin is designed to be threadsafe for adding and authenticating users
(see LoginServer). Note: If Seclogin’s Unit Tests are run twice in a row, it WILL fail second time. This
is the expected behavior. The first run of logins fills up the history file with good passwords in such
a way that the same passwords are not acceptable anymore.

Utility - This is another helper class. It contains various printing functions. The three currently
implemented are utility.message, utility.verbose_message and utility.debug. Change these properly
for more or less verbose input. By default utility.message sends its argument to the stdout, while
utility.debug silently discards it. Note, that for the multithreaded server to function properly on all
platforms (e.g. the IDLE environment on Linux), all stdout messages have to be muted – This is due
to a limitation in IDLE not being able to interpret passed stdout pointers.
IV.
Additional Information
We are using the following external modules/classes for programmatic support:
 Scipy + numpy
 Crypto
o Cipher
o Hash
o Util
 cPickle
For numpy (specifically, poly1d, a one-dimensional polynomial class) we use this during SecLogin for
encapsulating “natural” operations on polynomials so that the operations can take on their
customary form in code.
We use various functions from the Crypto (PyCrypto) module that allow us to instantiate an AES object,
symmetrical-key algorithm using a 32-byte key and a 16-byte block size. We also utilize the SHA256 oneway hash functions for various parts in the code and the Util module offer a number class that allow
convenient helped functions (e.g. long_to_bytes(...)).
cPickle is an extremely fast implementation of the pickle class for python, which provides a powerful
algorithm for serializing and de-serializing a Python object structure (In our case, the history file, has for
redundancy verification, and our instruction table). “Pickling” is the process whereby a Python object
hierarchy is converted into a byte stream, and “unpickling” is the inverse operation, whereby a byte stream
is converted back into an object hierarchy.
V.
Example
Below is a sample output of the program’s execution from init, to login failure, and a successful login. The
output is shown for clarification.
# ./main.py
$help
Printing options
init [--len_history|-l] len [--dir|-d] dir [--min|-m] min_len [--max|-M] max_len
initialize the system, with history_file size h(integer) and base directory d
(string: fully qualified path) and minimum password lengt min_len and maximum
password length max_len
load [--dir|-d] d: initialize the system by loading it from the config file,
found at the base directory d (string: fully qualified path)
add [--user|-u] username: add a new user with login name 'username' .
login [--user|-u] username: authenticate the user with login name 'username' .
exit: quit the program.
help: print this help menu.
$init -l 10 -d config -m 6 -M 20
Init a new system. Save config file.
SecLogin home created.
$add -u mustaque
Enter new password for user:
Renter new password for user:
Passwords did not match.
Enter new password for user:
Renter new password for user:
SecLogin: adding new user.
Initialized Instruction Table.
SecLogin: originally calculated pwd is
369815079354300349149468514366220179101352579562
Successfully added new user.
User successfuly added.
$login -u mustaque
Enter password for user:
SecLogin: log user in.
Initialized Instruction Table from file.
The unpickled object is invalid.
Authentication Failed.
Login Failed. Try Again.
Enter password for user:
SecLogin: log user in.
Initialized Instruction Table from file.
The unpickled object is invalid.
Authentication Failed.
Login Failed. Try Again.
Enter password for user:
SecLogin: log user in.
Initialized Instruction Table from file.
The unpickled object is invalid.
Authentication Failed.
Login Failed. Try Again.
Maximum number of attempts exceeded. Try again later.
Login Failed
$login -u mustaque
Enter password for user:
SecLogin: log user in.
Initialized Instruction Table from file.
SecLogin: calculated pwd is 369815079354300349149468514366220179101352579562
Signature correct.
History File initialized and deserialized.
SecLogin: login successful. Welcome!
Login Successful.
$exit
Thank you for using SecLogin!
Download