Monday, January 25, 2010

ISO 9797 algorithm 3

Last Friday I set out to implement ISO 9797 algorithm 3 using the OpenSSL library. I did not have the specification handy, so I decided to to the best I could with what I could find by way of examples on the net.


I came across a description of the algorithm in this 2005 thread (http://www.derkeiler.com/Newsgroups/sci.crypt/2005-02/0374.html). This was posted in a query by someone named Christian. He also posted his keys, data and the expected answer.


H0 = 0
stages 1 to n: Hj = Enc(K, Dj XOR H{j-1})
MAC = Enc(K, Dec(K', Hn))


Francois Grieu replied with, "This is very likely ISO/IEC 9797-1, using DES as the block cipher,
padding method 2, MAC algorithm 3." He provided an answer by sharing sample code in "some near-extinct dialect".


set m0 72C29C2371CC9BDB #message
set m1 65B779B8E8D37B29
set m2 ECC154AA56A8799F
set m3 AE2F498F76ED92F2

set pd 8000000000000000 #padding

set iv 0000000000000000 #initialisation vector

set k0 7962D9ECE03D1ACD #key
set k1 4C76089DCE131543

set xx {iv} # setup
for mj in {m0} {m1} {m2} {m3} {pd} # for each block including padding
set xx `xor {xx} {mj}` # chain
set xx `des -k {k0} -c {xx}` #encrypt
end
set xx `des -k {k1} -d {xx}` #decrypt
set xx `des -k {k0} -c {xx}` #encrypt
echo {xx} #show result

5F1448EEA8AD90A7


I've implemented the same in c for the purpose of research.


#include openssl/des.h
#include memory
#include string.h

//message + padding
const unsigned char msg[40] = { 0x72, 0xC2, 0x9C, 0x23, 0x71, 0xCC, 0x9B, 0xDB,
0x65, 0xB7, 0x79, 0xB8, 0xE8, 0xD3, 0x7B, 0x29,
0xEC, 0xC1, 0x54, 0xAA, 0x56, 0xA8, 0x79, 0x9F,
0xAE, 0x2F, 0x49, 0x8F, 0x76, 0xED, 0x92, 0xF2,
0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };

//initialization vector
unsigned char iv[8] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };

unsigned char k0[8] = { 0x79, 0x62, 0xD9, 0xEC, 0xE0, 0x3D, 0x1A, 0xCD };
unsigned char k1[8] = { 0x4C, 0x76, 0x08, 0x9D, 0xCE, 0x13, 0x15, 0x43 };


void print_hex(const unsigned char *bs, int n) {

for (int i = 0; i < n; i++)
printf("%02x", bs[i]);
printf("\n");
}

void des_ecb_crypt(unsigned char* input, unsigned char* output, int encrypt, unsigned char* key) {

des_key_schedule sched;
des_set_key((des_cblock *) key, sched);

DES_ecb_encrypt((const_DES_cblock *)input,
(const_DES_cblock *)output,
&sched,
encrypt);
}

void xor_block(unsigned char* src, unsigned char* dest) {

for (int x = 0; x < 8; x++) {
src[x] = src[x] ^ dest[x];
}
}

int main(int argc, char* argv[]) {

unsigned char output[8];
unsigned char xx[8];
unsigned char block[8];
int offset = 0;

memcpy(xx, iv, 8);

// Chain and encrypt 5 8-bit blocks
for (int x = 0; x < 5; x++) {

memcpy(block, &msg[offset] , 8);
offset+=8;

//set xx `xor {xx} {mj}` # chain
xor_block(xx, block);

//set xx `des -k {k0} -c {xx}` #encrypt
des_ecb_crypt(xx, output, DES_ENCRYPT, k0);
memcpy(xx, output, 8);
}


des_ecb_crypt(xx, output, DES_DECRYPT, k1);
memcpy(xx, output, 8);

des_ecb_crypt(xx, output, DES_ENCRYPT, k0);
memcpy(xx, output, 8);

print_hex(xx, 8);
return 1;
}

Friday, January 15, 2010

Finding hidden characters in file using Vim

Show newlines and tab location:

:set list

Back to normal:

:set nolist

View open file as hex:

:%!xxd

Tuesday, January 05, 2010

Apache, mod_wsgi, Django, SELinux

I've come to rely on the fact that if something isn't working after an installation on Centos 5, it is probably due to bad SELinux permissions. I've learned to live with SELinux and generally can handle any complication it throws at me. I've learned to keep an eye on /var/log/messages and /var/log/audit/audit.log. I've learned to use sealert and how to make my own local policies. Yet still it finds a way to throw me under the bus.

This time around I'm installing Django under Apache. I finally moved from mod_python to mod_wsgi. With mod_wsgi, less configuration is needed in httpd.conf, but more is needed in an external config file. For example, my original mod_python configuration looked like this:


<Directory "/var/www/html/python">
AddHandler mod_python .py
PythonHandler mptest
PythonDebug On
</Directory>

<Location "/">
SetHandler python-program
PythonHandler django.core.handlers.modpython
SetEnv DJANGO_SETTINGS_MODULE hsm.settings
PythonDebug On
PythonPath "['/home/stuff/src/python', '/home/stuff/src/python/hsm'] + sys.path"
</Location>


The second section is the Django deployment. (The first I included to remind myself how simple it is to deploy a very basic test program using mod_python. )

With mod_wsgi, the apache config looks like this. A single line identifies the location of the wsgi configuration, and the Directory element is used to give Apache permission to access the script.


WSGIScriptAlias / /usr/local/www/wsgi-scripts/hsm.wsgi

<Directory /usr/local/www/wsgi-scripts>
Order allow,deny
Allow from all
</Directory>



My custom wsgi script includes these lines. The last line keeps the wsgi handler from puking on
print lines that might be included in your file.


import os
import sys

os.environ['DJANGO_SETTINGS_MODULE'] = 'hsm.settings'

import django.core.handlers.wsgi
application = django.core.handlers.wsgi.WSGIHandler()

sys.stdout = sys.stderr


For this particular Django application, I decided to use sqlite3 instead of mysql. But attempting to launch proved problematic.



[Tue Jan 05 11:02:08 2010] [error] [client 10.8.8.62] return query.execute_sql(return_id)
[Tue Jan 05 11:02:08 2010] [error] [client 10.8.8.62] File "/usr/lib/python2.4/site-packages/django/db/models/sql/subqueries.py", line 320, in execute_sql
[Tue Jan 05 11:02:08 2010] [error] [client 10.8.8.62] cursor = super(InsertQuery, self).execute_sql(None)
[Tue Jan 05 11:02:08 2010] [error] [client 10.8.8.62] File "/usr/lib/python2.4/site-packages/django/db/models/sql/query.py", line 2369, in execute_sql
[Tue Jan 05 11:02:08 2010] [error] [client 10.8.8.62] cursor.execute(sql, params)
[Tue Jan 05 11:02:08 2010] [error] [client 10.8.8.62] File "/usr/lib/python2.4/site-packages/django/db/backends/util.py", line 19, in execute
[Tue Jan 05 11:02:08 2010] [error] [client 10.8.8.62] return self.cursor.execute(sql, params)
[Tue Jan 05 11:02:08 2010] [error] [client 10.8.8.62] File "/usr/lib/python2.4/site-packages/django/db/backends/sqlite3/base.py", line 193, in execute
[Tue Jan 05 11:02:08 2010] [error] [client 10.8.8.62] return Database.Cursor.execute(self, query, params)
[Tue Jan 05 11:02:08 2010] [error] [client 10.8.8.62] OperationalError: attempt to write a readonly database


To read and write to the sqlite database, I had to:

1. Ensure the database file and its parent directory was owned by the apache user. (The location of the database file is specified in the Django settings.py file for your project.)

2. Add a Directory entry to the httpd.conf file for the location of my database file. By default, apache does not have access to directories not under DocumentRoot.

3. Apply SELinux level permissions to the database file and its parent directory.

It took me quite awhile to discover that I had SELinux level permissions because I did not receive notifications in the /var/log/messages file. Normally all security exceptions arrive there and it is easy enough to tail the file and look for events. For whatever reason, the alerts were not appearing in the log, at least not every time. For example, the settroubleshoot alert browser (sealert -b) showed the exact problem that occurred at 11:02 AM (below), but the /var/log/messages file had no corresponding entry. The log did have a similar message from 10:59. The log either does not receive duplicate messages for SELinux events, or that some kind of bug is to blame.


Summary:

SELinux is preventing the httpd from using potentially mislabeled files
./sqlite3.db (usr_t).

Detailed Description:

SELinux has denied the httpd access to potentially mislabeled files
./sqlite3.db. This means that SELinux will not allow httpd to use these files.
Many third party apps install html files in directories that SELinux policy
cannot predict. These directories have to be labeled with a file context which
httpd can access.

Allowing Access:

If you want to change the file context of ./sqlite3.db so that the httpd daemon
can access it, you need to execute it using chcon -t httpd_sys_content_t
'./sqlite3.db'. You can look at the httpd_selinux man page for additional
information.



The solution was to modify the SELinux permissions on the folder that contained the sqlite database:


sudo chcon -R system_u:object_r:httpd_sys_content_t database_folder


Hope this information will help the next unlucky person to deploy in a similar environment.