Implementing the Khazad Block Cipher in Perl

The Perl Journal August 2003

By Julius C. Duque

Julius is a freelance network consultant in the Philippines. He can be contacted at jcduque@lycos.com.
The Khazad Block Cipher

Khazad is a new 64-bit block cipher that accepts a 128-bit key. Invented by Paulo S.L.M. Barreto and Vincent Rijmen, it is a finalist in the New European Schemes for Signatures, Integrity, and Encryption (NESSIE) Project.

In this article, I will show you how I implemented Khazad in Perl using XS programming. Using the techniques presented here, I hope that you will also be able to implement any block cipher that you fancy (or, perhaps, just create an XS-based Perl module).

At this writing, the current version of Crypt::Khazad is 1.0.3. It is available on CPAN at http://www.cpan.org/authors/id/J/JC/JCDUQUE/Crypt-Khazad-1.0.3.tar.gz. Files mentioned in this article are available in this module distribution. Also see http:// planeta.terra.com.br/informatica/paulobarreto/KhazadPage.html

Using h2xs

The first thing to do is create a subdirectory where the module will be created and stored using the program h2xs, which converts C header files to Perl extensions. On the command line, type:

h2xs --omit-autoload --omit-constant --name=Crypt::Khazad

Refer to the h2xs man pages for explanation of the options --omit-autoload and --omit-constant. What's important here is the option --name=Crypt::Khazad. This tells h2xs to create a subdirectory Crypt/Khazad/.

The following files will be created in Crypt/Khazad/:

Changes 
Khazad.pm 
Khazad.xs 
MANIFEST 
Makefile.PL 
README 
ppport.h 
t

"t," which stands for tests, is a directory containing test scripts. For the moment, there's only one test script found here (1.t). All test scripts are placed in this directory and must be suffixed with '.t.'

The typemap File

Change directories (cd) into Crypt/Khazad. What is lacking in this directory is the typemap file. Create this file, and add the following line:

Crypt::Khazad T_PTROBJ

Note that the whitespace separating Crypt::Khazad and T_PTROBJ must be a tab.

The typemap file contains the mapping of Crypt::Khazad, an arbitrary data type, to its corresponding Perl value, T_PTROBJ, a type representing a pointer to a structure. Thus, we can set up an encryption/decryption routine like this:

use  Crypt::Khazad; 
$cipherobj  =  new  Crypt::Khazad  $key; 
$ciphertext  =  $cipherobj->encrypt($plaintext); 
$plaintext   =  $cipherobj->decrypt($ciphertext); 

Given an argument, $key, the new() function will create an object, $cipherobj, of type Crypt::Khazad (the data type we just declared in the typemap file).

Essentially, new() clones the object Crypt::Khazad. In most object-oriented languages, new() is already a built-in function. Unfortunately, there is no equivalent function in Perl. So, we have to create one ourselves. Since we are free to implement this function in any way we want, we could just as easily name it, say, clone(), and we would still get the same result.

Now, comes the fun part: XS programming!

XS Programming

Another missing file in Crypt/Khazad/ is the heart of the module, the Khazad C code. In our case, that file is named _khazad.c (it's available in the Crypt::Khazad module distribution on CPAN). So copy this file to Crypt/Khazad/.

The best way to understand C code is to study its main() function. In _khazad.c, we see how main() uses the other functions to perform the process of encryption and decryption. (See Listing 1.)

In Line 3, we see that variable subkeys is declared as data type NESSIEstruct. Lines 14-16 tell us that we have to initialize subkeys with a call to NESSIEkeysetup() before calling the functions NESSIEencrypt() for encryption, and NESSIEdecrypt() for decryption.

Now, open the file Khazad.xs. Initially, its content is just:

1      #include  "EXTERN.h" 
2      #include  "perl.h" 
3      #include  "XSUB.h" 
4  
5      #include  "ppport.h" 
6  
7      MODULE  =  Crypt::Khazad         PACKAGE  =  Crypt::Khazad 

Edit Khazad.xs so that it becomes:

1      #include  "EXTERN.h" 
2      #include  "perl.h" 
3      #include  "XSUB.h" 
4  
5      #include  "ppport.h" 
6  
7      #include  "_khazad.c" 
8  
9      typedef  struct  khazad  { 
10           NESSIEstruct  key; 
11       }*  Crypt__Khazad; 
12   
13       MODULE  =  Crypt::Khazad  PACKAGE  =  Crypt::Khazad 

Notice that we have now #include-d the C code, _khazad.c (line 7). What about lines 9-11, starting with typedef struct khazad? Because the C code declares subkeys as type NESSIEstruct (line 3 of Listing 1), we have to declare the variable key in Khazad.xs (line 10) as type NESSIEstruct as well.

But there's a catch. When _khazad.c is compiled, everything is fine. But when the XS file is compiled by xsubpp, it complains. In fact, you are likely to get the following fatal errors:

Khazad.c:  In  function  `XS_Crypt__Khazad_new': 
Khazad.c:64:  `Crypt__Khazad'  undeclared  (first  use  in this  function) 
Khazad.c:64:  (Each  undeclared  identifier  is  reported  only once 
Khazad.c:64:  for  each  function  it  appears  in.) 

The solution is to look for the NESSIEstruct line in _khazad.c, and check how NESSIEstruct is declared. That code snippet is as follows:

struct  NESSIEstruct  { 
    u32  roundKeyEnc[R  +  1][2]; 
    u32  roundKeyDec[R  +  1][2]; 
}; 

The xsubpp compiler does not like this code. So, revise this code into this:

typedef  struct  NESSIEstruct  { 
    u32  roundKeyEnc[R  +  1][2]; 
    u32  roundKeyDec[R  +  1][2]; 
}  NESSIEstruct; 

That will keep the xsubpp compiler happy.

Ensuring Crypt::CBC Compliance

Crypt::CBC is a module developed by Lincoln Stein to be used specifically in conjunction with block ciphers. As with all Perl modules, Crypt::CBC is also available from http://search.cpan.org/.

To be Crypt::CBC compliant, our block cipher must be able to return the block size it is using, as well as the length of its key, when Crypt::CBC asks for them. Edit Khazad.xs, adding the following lines:

int 
keysize(...) 
CODE: 
    RETVAL  =  16; 
OUTPUT: 
    RETVAL 

int 
blocksize(...) 
CODE: 
    RETVAL  =  8; 
OUTPUT: 
    RETVAL 

Our new Khazad.xs is transformed as shown in Listing 2.

The keyword int must be on a line by itself. RETVAL stands for "return value" and this is the data that Crypt::CBC needs. Read the perlxstut man pages for more details. The keysize() function returns a value of 16 bytes (the 128-bit key), while blocksize() returns 8 bytes (the 64-bit block length).

If we have the following code snippet:

use  Crypt::Khazad; 
  
$cipherobj  =  new  Crypt::Khazad  $key; 
$ks  =  $cipherobj->keysize(); 
$bs  =  $cipherobj->blocksize(); 

$ks should hold the value 16, and $bs should be 8.

To implement the new(), encrypt(), and decrypt() functions, we must be familiar with perlguts and perlxs.

Edit Khazad.xs. It should now look like Listing 3. You'll want to consult the perlguts man pages for discussions of the following keywords:

SV* 
STRLEN 
SvPOK 
SvCUR 
Newz 
SvPV _nolen 
SvPV 
newSVpv 

A couple of important things are worth mentioning here. The new() function actually calls the C function NESSIEkeysetup(). Similarly, encrypt() and decrypt() execute the C functions NESSIEencrypt() and NESSIEdecrypt(), respectively. Just refer to the main() function of _khazad.c (Listing 1) to learn how these functions are used.

The DESTROY() function is special. This function is called when the object, Crypt::Khazad, goes out of scope and needs to be destroyed. If the DESTROY() function does not exist, then nothing is done, and a memory leak could occur.

The Khazad.pm File

Delete the Khazad.pm generated by h2xs. We will make our own from scratch. Listing 4 is our new Khazad.pm.

It is worth noting that variable @EXPORT_OK (line 7) should list only those functions that we want to be visible from outside of the Khazad module. Line 7 tells us that keysize(), blocksize(), new(), encrypt(), and decrypt() are the only functions of Khazad that are accessible by other modules.

The Makefile.PL File

Delete the generated Makefile.PL. We'll make our own instead. The following is our hand-crafted Makefile.PL:

1  use  ExtUtils::MakeMaker; 
2  
3  WriteMakefile( 
4      'NAME'  =>  'Crypt::Khazad', 
5      'VERSION_FROM'  =>  'Khazad.pm', 
6      'PREREQ_PM'  =>  {}, 
7      'AUTHOR'  =>  'Julius  C.  Duque', 
8      'LIBS'  =>  [''], 
9      'DEFINE'  =>  '', 
10       'INC'  =>  '-I.', 
11       'dist'  =>  {'COMPRESS'  =>  'gzip  -9f',  'SUFFIX'  =>  'gz'} 
12   ); 

Please read the perlxstut man pages for more details.

Writing a Test Script

Every Perl module should be accompanied by at least one test script. A sample test script is shown in Listing 5. Place this script in the "t" directory. The test script must be suffixed with a ".t." (If you want to know more on how to make test scripts, read up on Test::More.)

Preparing the Distribution

This is the easiest part. Just type the following:

perl Makefile.PL   
make manifest   
make dist 

The module is now bundled as a *.tar.gz file.

How to Use Khazad

An example of using Khazad in a Crypt::CBC-compliant code is shown in Listing 6. The output should be:

plaintext1   :  0123456789abcdeffedcba9876543210 
ciphertext   :  1be2bb1a7b1bfa4227e33b06cf45c2d0f942f14a5b414e41 
plaintext2   :  0123456789abcdeffedcba9876543210 

Other Crypt Modules

In addition to Crypt::Khazad, I have created several other block cipher modules. All of these are available on CPAN:

Crypt::Misty1 
Crypt::Anubis 
Crypt::Noekeon 
Crypt::Skipjack 
Crypt::Camellia 
Crypt::Square 

References

The following man pages are essential reading: h2xs, perlxstut, perlxs, and perlguts.

The following books also got me going: Perl 5 How-To by M. Glover, A. Humphreys, and E. Weiss (Waite Group; ASIN 1571690581).

Perl Cookbook. T. Christiansen and N. Torkington (O'Reilly & Associates; ISBN 1565922433).

Acknowledgments

I am indebted to Marc Lehmann for his Crypt::Twofish2 module. I used his module as a skeleton for the Khazad implementation. Many thanks also go to the inventors of Khazad, Paulo S.L.M. Barreto and Vincent Rijmen. The Khazad home page is at http://planeta.terra.com.br/informatica/paulobarreto/KhazadPage.html.

TPJ

Listing 1

1      int  main(void) 
2      { 
3        struct  NESSIEstruct  subkeys; 
4        u8  key[KEYSIZEB]; 
5        u8  plain[BLOCKSIZEB]; 
6        u8  cipher[BLOCKSIZEB]; 
7        u8  decrypted[BLOCKSIZEB]; 
8  
9        int  v; 
10   
11         printf("Test  vectors  --  set  1\n"); 
12         printf("=====================\n\n"); 
13   
14         NESSIEkeysetup(key,  &subkeys); 
15         NESSIEencrypt(&subkeys,  plain,  cipher); 
16         NESSIEdecrypt(&subkeys,  cipher,  decrypted); 
17   
18         /******  more  lines  follow  ******/ 

Back to Article

Listing 2

1     #include  "EXTERN.h" 
2     #include  "perl.h" 
3     #include  "XSUB.h" 
4  
5     #include  "ppport.h" 
6  
7     #include  "_khazad.c" 
8  
9     typedef  struct  khazad  { 
10     NESSIEstruct  key; 
11      }*  Crypt__Khazad; 
12   
13      MODULE  =  Crypt::Khazad    PACKAGE  =  Crypt::Khazad
14   
15      int 
16      keysize(...) 
17     CODE: 
18    RETVAL  =  16; 
19     OUTPUT: 
20    RETVAL 
21   
22      int 
23      blocksize(...) 
24     CODE: 
25    RETVAL  =  8; 
26     OUTPUT: 
27    RETVAL 

Back to Article

Listing 3

1  #include  "EXTERN.h" 
2  #include  "perl.h" 
3  #include  "XSUB.h" 
4  
5  #include  "ppport.h" 
6  
7  #include  "_khazad.c" 
8  
9  typedef  struct  khazad  { 
10       NESSIEstruct  key; 
11   }*  Crypt__Khazad; 
12   
13   MODULE  =  Crypt::Khazad         PACKAGE  =  Crypt::Khazad 
14   
15   int 
16   keysize(...) 
17       CODE: 
18           RETVAL  =  16; 
19       OUTPUT: 
20           RETVAL 
21   
22   int 
23   blocksize(...) 
24       CODE: 
25           RETVAL  =  8; 
26       OUTPUT: 
27           RETVAL 
28   
29   Crypt::Khazad 
30   new(class,  rawkey) 
31       SV*  class 
32       SV*  rawkey 
33       CODE: 
34       { 
35           STRLEN  keyLength; 
36           if  (!  SvPOK(rawkey)) 
37               croak("Error:  Key  must  be  a  string  scalar!"); 
38   
39           keyLength  =  SvCUR(rawkey); 
40           if  (keyLength  !=  16) 
41               croak("Error:  Key  must  be  16  bytes  long!"); 
42   
43           Newz(0,  RETVAL,  1,  struct  khazad); 
44           NESSIEkeysetup(SvPV_nolen(rawkey),  &RETVAL->key); 
45       } 
46   
47       OUTPUT: 
48           RETVAL 
49   
50   SV* 
51   encrypt(self,  input) 
52       Crypt::Khazad  self 
53       SV*  input 
54       CODE: 
55       { 
56           STRLEN  blockSize; 
57           unsigned  char*  intext  =  SvPV(input,  blockSize); 
58           if  (blockSize  !=  8)  { 
59               croak("Error:  Block  size  must  be  8  bytes  long!"); 
60           }  else  { 
61               RETVAL  =  newSVpv("",  blockSize); 
62               NESSIEencrypt(&self->key,  intext,  SvPV_nolen(RETVAL)); 
63           } 
64       } 
65   
66       OUTPUT: 
67           RETVAL 
68   
69   SV* 
70   decrypt(self,  input) 
71       Crypt::Khazad  self 
72       SV*  input 
73       CODE: 
74       { 
75           STRLEN  blockSize; 
76           unsigned  char*  intext  =  SvPV(input,  blockSize); 
77           if  (blockSize  !=  8)  { 
78               croak("Error:  Block  size  must  be  8  bytes  long!"); 
79           }  else  { 
80               RETVAL  =  newSVpv("",  blockSize); 
81               NESSIEdecrypt(&self->key,  intext,  SvPV_nolen(RETVAL)); 
82           } 
83       } 
84   
85       OUTPUT: 
86           RETVAL 
87   
88   void 
89   DESTROY(self) 
90       Crypt::Khazad  self 
91       CODE: 
92           Safefree(self); 

Back to Article

Listing 4

1  package  Crypt::Khazad; 
2  
3  use  strict; 
4  use  warnings; 
5  require  Exporter; 
6  
7  our  @EXPORT_OK  =  qw(keysize  blocksize  new  encrypt  decrypt); 
8  our  $VERSION  =  '1.0.0'; 
9  our  @ISA  =  qw(Exporter); 
10   
11   require  XSLoader; 
12   XSLoader::load('Crypt::Khazad',  $VERSION); 
13   
14   #  Preloaded  methods  go  here. 
15   
16   1; 
17   
18   __END__ 
19   
20   =head1  NAME 
21   
22   Crypt::Khazad  -  Crypt::CBC-compliant  block  cipher 
23   
24   =head1  ABSTRACT 
25   
26   Put  abstract  here. 
27   
28   =head1  SYNOPSIS 
29   
30       use  Crypt::Khazad; 
31       $cipher  =  new  Crypt::Khazad  $key; 
32       $ciphertext  =  $cipher->encrypt($plaintext); 
33       $plaintext   =  $cipher->decrypt($ciphertext); 
34   
35   =head1  DESCRIPTION 
36   
37   Put  description  here. 
38   
39   =head1  COPYRIGHT  AND  LICENSE 
40   
41   Put  copyright  notice  here. 
42   
43   =cut 

Back to Article

Listing 5

1  use  diagnostics; 
2  use  strict; 
3  use  warnings; 
4  use  Test::More  tests  =>  2; 
5  BEGIN  { 
6      use_ok('Crypt::Khazad') 
7  }; 
8  
9  BEGIN  { 
10       my  $key  =  pack  "H32",  "80000000000000000000000000000000"; 
11       my  $cipher  =  new  Crypt::Khazad  $key; 
12       my  $plaintext  =  pack  "H16",  "0000000000000000"; 
13       my  $ciphertext  =  $cipher->encrypt($plaintext); 
14       my  $answer  =  unpack  "H*",  $ciphertext; 
15       is("49a4ce32ac190e3f",  $answer); 
16   }; 

Back to Article

Listing 6

1  #!/usr/local/bin/perl 
2  
3  use  diagnostics; 
4  use  strict; 
5  use  warnings; 
6  use  Crypt::CBC;     #  CBC  automatically  loads  Khazad  for  us 
7  
8  my  $key  =  pack  "H32",  "00112233445566778899aabbccddeeff"; 
9  my  $IV  =  pack  "H16",  "0102030405060708"; 
10   
11   my  $cipher  =  Crypt::CBC->new({'key'  =>  $key, 
12                                 'cipher'  =>  'Khazad', 
13                                 'iv'  =>  $IV, 
14                                 'regenerate_key'  =>  1, 
15                                 'padding'  =>  'standard', 
16                                 'prepend_iv'  =>  0 
17                               }); 
18   
19   my  $plaintext1  =  pack  "H32",  "0123456789abcdeffedcba9876543210"; 
20   print  "plaintext1   :  ",  unpack("H*",  $plaintext1),  "\n"; 
21   
22   my  $ciphertext  =  $cipher->encrypt($plaintext1); 
23   print  "ciphertext   :  ",  unpack("H*",  $ciphertext),  "\n"; 
24   
25   my  $plaintext2  =  $cipher->decrypt($ciphertext); 
26   print  "plaintext2   :  ",  unpack("H*",  $plaintext2),  "\n"; 




Back to Article