Crypto-tools

Introduction

The crypto-tools egg implements useful cryptographic primitives. More specifically, provided are:

Block cipher modes

Naturally, this are higher-order functions; the underlying cipher 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 cipher 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 ciphertext (encrypted in CBC mode and plaintext in CTR).

In CBC mode if the resulting ciphertext is not a multiple of the block size, as it must be padded to that length. The desired padding algorithms can be provided to the make-cbc functions in order for the pad to be added or removed automatically. CTR mode does not require padding.

For more details on how the modes are specified one can refer to Wikipedia.

Examples

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

(import crypto-tools)
(blob->hexstring (string->blob "Hello, world!"))
 => "48656c6c6f2c20776f726c6421"

(blob->hexstring/uppercase (string->blob "Hello, world!"))
 => "48656C6C6F2C20776F726C6421"

(import srfi-4)
(blob->u8vector (hexstring->blob "010203"))
 => #u8(1 2 3)

XORing blobs is easy (if they are not of equal length the operation goes up to the length of the smaller one):

(blob->u8vector (blob-xor (hexstring->blob "010203") (hexstring->blob "000200")))
 => #u8(1 0 3)

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...)

(blob->hexstring (blob-pad (hexstring->blob "010203") 16))
 => "01020380000000000000000000000000"

(blob->hexstring (blob-unpad (hexstring->blob "01020380000000000000000000000000")))
 => "010203"

(blob->hexstring (blob-pad (hexstring->blob "") 16))
 => "80000000000000000000000000000000"

(blob->hexstring (blob-unpad (hexstring->blob "80000000000000000000000000000000")))
 => ""

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

(import aes)
(import crypto-tools)

(define key "1234567890abcdeffedcba0987654321")
(define iv (hexstring->blob "000000001234567890abcabcab010101"))
(define aenc (make-aes128-encryptor (hexstring->blob key)))
(define adec (make-aes128-decryptor (hexstring->blob key)))
(define cbc-enc (make-cbc*-encryptor aenc blob-pad 16))
(define cbc-dec (make-cbc*-decryptor adec blob-unpad 16))
(define cbc-ct (blob->hexstring (cbc-enc (string->blob "Hello there crypto-tools!") iv)))
(blob->string (cbc-dec (hexstring->blob cbc-ct)))
=> "Hello there crypto-tools!"

Also a similar operation in CTR mode but this time without storing the IV in the ciphertext. Observe that in CTR mode we only use the encryptor for both encrypt and decrypt operations and padding is not required.

(import aes)
(import crypto-tools)

(define key "1234567890abcdeffedcba0987654321")
(define iv (hexstring->blob "000000001234567890abcabcab010101"))
(define aenc (make-aes128-encryptor (hexstring->blob key)))
(define ctr-enc (make-ctr-encryptor aenc 16))
(define ctr-dec (make-ctr-decryptor aenc 16))
(define ctr-ct (blob->hexstring (ctr-enc (string->blob "Hello there CTR crypto-tools!") iv)))
(blob->string (ctr-dec (hexstring->blob ctr-ct) iv))
=> "Hello there CTR crypto-tools!"

Library functions

[procedure] (blob->hexstring BLOB) => STRING

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

[procedure] (blob->hexstring/uppercase BLOB) => STRING

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

[procedure] (hexstring->blob STRING) => BLOB

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

Given two blobs, returns a blob of the size of the smaller input obtained by bitwise XORing the two input blobs together.

[procedure] (blob-pad BLOB SIZE) => BLOB

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. Conforms to ISO7816-4.

[procedure] (blob-unpad BLOB) => BLOB

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] (blob-pkcs5-pad BLOB SIZE) => BLOB

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 a value of how many bytes comprise the pad. Conforms to ISO7816-4.

[procedure] (blob-pkcs5-unpad BLOB) => BLOB

Reverses the effect of the previous function, by reading the last byte and then removing as many as its value suggest.

[procedure] (make-cbc-encryptor ENCRYPTOR PADFN BLOCKSIZE) => PROCESSOR

Given a block encryption function that accepts and returns blobs of BLOCKSIZE bytes and a padding function, 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 UNPADFN BLOCKSIZE) => PROCESSOR

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

[procedure] (make-cbc*-encryptor ENCRYPTOR PADFN BLOCKSIZE) => PROCESSOR

Given a block encryption function that accepts and returns blobs of BLOCKSIZE bytes and a padding function, 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 ciphertext.

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

[procedure] (make-cbc*-decryptor ENCRYPTOR UNPADFN BLOCKSIZE) => PROCESSOR-WITHOUT-IV

Given a block decryption function that accepts and returns blobs of BLOCKSIZE bytes and an unpadding function, returns a CBC processor using the supplied decryptor that will decrypt arbitrary blobs, stripping off padding and reading its IV from the first block.

[procedure] (make-ctr-encryptor ENCRYPTOR BLOCKSIZE) => PROCESSOR

Given a block encryption function that accepts and returns blobs of BLOCKSIZE bytes, returns a CTR processor using the supplied encryptor that will encrypt arbitrary blobs.

[procedure] (make-ctr-decryptor ENCRYPTOR BLOCKSIZE) => PROCESSOR

Given a block decryption function that accepts and returns blobs of BLOCKSIZE bytes, returns a CTR processor using the supplied encryptor that will decrypt arbitrary blobs.

[procedure] (make-ctr*-encryptor ENCRYPTOR BLOCKSIZE) => PROCESSOR

Given a block encryption function that accepts and returns blobs of BLOCKSIZE bytes, returns a CTR processor using the supplied encryptor that will encrypt arbitrary blobs, and will include an copy of the IV counter in the ciphertext.

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

[procedure] (make-ctr*-decryptor ENCRYPTOR BLOCKSIZE) => PROCESSOR-WITHOUT-IV

Given a block decryption function that accepts and returns blobs of BLOCKSIZE bytes, returns a CTR processor using the supplied encryptor that will decrypt arbitrary blobs and will read its IV from the first block.

[procedure] (PROCESSOR BLOB IV) => BLOB

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] (PROCESSOR-WITHOUT-IV BLOB) => BLOB

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

Authors

Alaric B. Snell-Pym

Repository

This egg is hosted on the CHICKEN Subversion repository:

https://anonymous@code.call-cc.org/svn/chicken-eggs/release/5/crypto-tools

If you want to check out the source code repository of this egg and you are not familiar with Subversion, see this page.

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