[PYTHON-CRYPTO] aes library, take 2

Bram Cohen bram at GAWTH.COM
Sat Mar 30 02:49:42 CET 2002

This is a proposal for the API for a module called 'aes', which implements
the standard modes of rijndael.

The purpose of this module is *not* to be a complete library for all the
standard things one might want to do with aes. Rather, it's a bare-bones
library which does just enough to make it possible to implement all the
standard aes stuff efficiently in 'pure' Python, with the only C calls
being to this library.

For example, ciphertext stealing in CBC mode is to be implemented using
aes.CBC with a few calls at the end to aes.ECB to do the stealing part.
This results in the performance benefits of using C without the API bloat
of doing *everything* in C. Padding modes may be added to the aes module
in a backwards-compatible way in the future, but that's a can of worms to
be opened at a later time.

The methods of AES are ECB, CTR, OFB, CBC, and CFB. Each of them takes a
16, 24, or 32 byte key and returns an encryption object.

ECB encryption objects have two methods, encrypt and decrypt, both of
which take a string whose length is a multiple of 16 bytes and return an
encrypted or decrypted string of the same length. Strings of length 0 are

CTR encryption objects have a method called process, which takes a counter
(an integer), a string to be encrypted, and an optional bigendian
parameter. CTR is the same step for encryption and decryption, hence the
single process function instead of separate encrypt and decrypt functions.
process() returns a string of the same length as the one it received.

The counter passed in to process() is modulo 2 ** 128, so numbers such as
-1 are allowed. When the counter gets passed 2 ** 128 it should roll back
to zero.

The bigendian parameter determines whether encoding of the counter into a
block to be encrypted is done big or little endian. It defaults to 1

The other method of CTR objects is processor(), which takes a counter and
optional bigendian parameter which defaults to 1 and returns a function.
The function takes strings and returns each of them encrypted
incrementally. This is the same as using the process() method only it
isn't necessary to have the entire string in memory at once. The following
test should always pass -

def test_concat(key, s):
    x = CTR(key)
    f = x.processor(0)
    assert x.process(0, s) == f(s[:3]) + f(s[3:])

OFB has the same API as CTR except that instead of a counter it takes an
iv of length 16, and it has no bigendian parameter.

CBC has a similar API to OFB, except that instead of process and processor
functions, it has analogous encrypt and encrypter() and also an analagous
decrypt and decrypter(). In these modes incremental processing sometimes
has to buffer a bit before it knows how to encrypt the next byte, so they
return as long a string as they can from each call to incremental
processing functions, never trailing the amount of data passed in by 16
bytes or more.

CFB objects have encrypter() and decrypter()  functions which take an IV,
and both of them return a function which accepts strings and returns
strings of the same length. Note that unlike CTR and OFB, CFB decryption
*must* be subdivided into the exact same block lengths as it's encryption
was, or it's decryption won't work.


    process(counter, str, bigendian = 1)
    processor(counter, bigendian = 1)

    process(iv, str)

    encrypt(iv, str)
    decrypt(iv, str)


-Bram Cohen

"Markets can remain irrational longer than you can remain solvent"
                                        -- John Maynard Keynes

More information about the python-crypto mailing list