Note: This is taken from the Chicken Wiki, where a more recent version could be available.

Introduction

The crypto-tools egg implements fundamental cryptographic operations. Currently, the main focus of this is an implementation of CBC (Cyphertext Block Chaining) and block padding, a technique that can be used to convert any block-based cypher into one capable of securely encrypting arbitrary-lengthed blobs.

Naturally, this is a higher-order function; the underlying cypher must be provided as an encryption or decryption function that accepts a blob of a fixed size and returns an encrypted or decrypted blob of the same size. That function, along with the size of blob it works with, are passed to `make-cbc-encryptor` or `make-cbc-decryptor`, and it returns a function that encrypts or decrypts a blob of any size, given an "initialisation vector" or IV (a blob of the cipher's block size). The IV, in addition to whatever key the underlying cypher uses, must be the same for decryption as for encryption in order to get a successful result; the IV can either be derived as part of the key, or randomly generated before encryption then stored along with the cyphertext (perhaps encrypted).

Note that the result of CBC encryption will always be slightly larger than the input, as it must be padded to the block size. If the input block is already an integral number of blocks long, then an entire extra padding block is required in order to make the padding invertible. The decryption operation automatically reverses the padding.

This egg also provides the underlying block-padding and unpadding operations, along with a few functions that may be of use in other cryptographic operations: exclusive-oring blobs together, and converting blobs to and from hexadecimal strings, which can be useful for storing keys in human-readable form.

Examples

Firstly, the functions to convert between blobs and hex strings:

<examples> <example> <expr>(use crypto-tools)</expr> <expr>(blob→hexstring (string→blob "Hello, world!"))</expr> <result>"48656c6c6f2c20776f726c6421"</result> </example> <example> <expr>(blob→hexstring/uppercase (string→blob "Hello, world!"))</expr> <result>"48656C6C6F2C20776F726C6421"</result> </example> <example> <expr>(use srfi-4)</expr> <expr>(blob→u8vector (hexstring→blob "010203"))</expr> <result>#u8(1 2 3)</result> </example> </examples>

XORing blobs is easy (as long as they both have the same length):

<examples> <example> <expr>(blob→u8vector (blob-xor (hexstring→blob "010203") (hexstring→blob "000200")))</expr> <result>#u8(1 0 3)</result> </example> </examples>

The padding system can pad a blob to a fixed block size, as long as the blob is at most one byte shorter than the desired block size. It's up to you to split an input message into blocks then only attempt to pad the last block. If the input message is an integral number of blocks big, you should pad a zero-length block and make that the last block, unless you have an out-of-band method for indicating that the last block is not padded (but if you had that, you could use it to send a length, then pad the last block with random data, and on receipt, trim the result to the supplied length, removing the need for reversible padding anyway...)

<examples> <example> <expr>(blob→hexstring (blob-pad (hexstring→blob "010203") 16))</expr> <result>"01020380000000000000000000000000"</result> </example> <example> <expr>(blob→hexstring (blob-unpad (hexstring→blob "01020380000000000000000000000000")))</expr> <result>"010203"</result> </example> <example> <expr>(blob→hexstring (blob-pad (hexstring→blob "") 16))</expr> <result>"80000000000000000000000000000000"</result> </example> <example> <expr>(blob→hexstring (blob-unpad (hexstring→blob "80000000000000000000000000000000")))</expr> <result>""</result> </example> </examples>

Then, finally, we can get to the meat of it - CBC:

<examples> <example> <expr>(use aes)</expr> <expr>(define encryptor (make-aes128-encryptor (hexstring→blob "00010203050607080A0B0C0D0F101112")))</expr> <expr>(define decryptor (make-aes128-decryptor (hexstring→blob "00010203050607080A0B0C0D0F101112")))</expr> <expr>(define cbc-encryptor (make-cbc-encryptor encryptor 16))</expr> <expr>(define cbc-decryptor (make-cbc-decryptor decryptor 16))</expr> <expr>(define iv (hexstring→blob "00010203050607080A0B0C0D0F101112"))</expr> <expr>(blob→hexstring (cbc-encryptor (string→blob "Hello, World!") iv))</expr> <result>"06acc6a8d17dec1563ac99f9a266bceb"</result> <expr>(blob→string (cbc-decryptor (hexstring→blob "06acc6a8d17dec1563ac99f9a266bceb") iv))</expr> <result>"Hello, World!"</result> </example> </examples>

Library functions

<procedure>(blob→hexstring BLOB) ⇒ STRING</procedure>

Takes an arbitrary blob, and returns a string made by encoding the blob in hexadecimal.

<procedure>(blob→hexstring/uppercase BLOB) ⇒ STRING</procedure>

As above, but using `ABCDEF` rather than `abcdef` in the string representation.

<procedure>(hexstring→blob STRING) ⇒ BLOB</procedure>

Creates a blob from a hexadecimal string. If the string is not even-lengthed, or contains characters outside of `[0-9a-fA-F]`, an error is signalled.

<procedure>(blob-xor BLOB BLOB) ⇒ BLOB</procedure>

Given two blobs of the same size, returns a blob of that size obtained by bitwise XORing the two input blobs together. Useful for chaining in cyphertext for each block in a CBC operation.

<procedure>(blob-pad BLOB SIZE) ⇒ BLOB</procedure>

Given a blob of at most SIZE-1 bytes, and a desired size, returns a blob of SIZE bytes by appending padding to the end of the input blob. The padding is a byte with the high bit set and no other bits set, followed by enough all-zero bytes to fill the block.

<procedure>(blob-unpad BLOB) ⇒ BLOB</procedure>

Reverses the effect of the previous function, by removing zero bytes from the end of the supplied blob, then removing a single byte with only the high bit set. If the input blob does not have valid padding at the end, an error is signalled.

<procedure>(make-cbc-encryptor ENCRYPTOR BLOCKSIZE) ⇒ PROCESSOR</procedure>

Given a block encryption function that accepts and returns blobs of BLOCKSIZE bytes, returns a CBC processor using the supplied encryptor that will encrypt arbitrary blobs, making them larger with padding in the process.

<procedure>(make-cbc-decryptor ENCRYPTOR BLOCKSIZE) ⇒ PROCESSOR</procedure>

Given a block decryption function that accepts and returns blobs of BLOCKSIZE bytes, returns a CBC processor using the supplied decryptor that will decrypt arbitrary blobs, stripping off padding.

<procedure>(PROCESSOR BLOB IV) ⇒ BLOB</procedure>

Given a blob of arbitrary size and an initialisation vector represented as a blob of BLOCKSIZE bytes, returns an encrypted/decrypted blob as appropriate.

<procedure>(make-cbc*-encryptor ENCRYPTOR BLOCKSIZE) ⇒ PROCESSOR</procedure>

Given a block encryption function that accepts and returns blobs of BLOCKSIZE bytes, returns a CBC processor using the supplied encryptor that will encrypt arbitrary blobs, making them larger with padding in the process, and including an encrypted copy of the IV in the cyphertext.

Although knowing the IV is not an advantage in itself to an attacker - after all, every encrypted block in the output is the IV for the rest of the encryption - we encrypt the IV as a precautionary measure; the process of generating an IV may reveal the state of entropy sources at the encoder, the current time, etc, which may be of interest to an attacker.

The result of encryption will be one block larger than normal CBC encryption, due to the embedded IV.

<procedure>(make-cbc*-decryptor ENCRYPTOR BLOCKSIZE) ⇒ PROCESSOR-WITHOUT-IV</procedure>

Given a block decryption function that accepts and returns blobs of BLOCKSIZE bytes, returns a CBC processor using the supplied decryptor that will decrypt arbitrary blobs, stripping off padding.

<procedure>(PROCESSOR-WITHOUT-IV BLOB) ⇒ BLOB</procedure>

Given a blob returned by a make-cbc*-encryptor processor, returns a decrypted blob.

Authors

Alaric B. Snell-Pym

License

 Copyright (c) 2003-2009, Warhead.org.uk Ltd
 All rights reserved.
 Redistribution and use in source and binary forms, with or without
 modification, are permitted provided that the following conditions
 are met:
 Redistributions of source code must retain the above copyright
 notice, this list of conditions and the following disclaimer.
 Redistributions in binary form must reproduce the above copyright
 notice, this list of conditions and the following disclaimer in the
 documentation and/or other materials provided with the distribution.
 Neither the names of Warhead.org.uk Ltd, Snell Systems, nor Kitten
 Technologies, nor the names of their contributors may be used to
 endorse or promote products derived from this software without
 specific prior written permission.
 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
 FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
 COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
 INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
 BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
 CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
 ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 POSSIBILITY OF SUCH DAMAGE.

Requirements

None

Version History