Data Encryption Standard: Part 2
September 3, 2010
We begin with utility functions that pad and unpad text and read the next eight bytes from a file:
(define (pad txt)
(let ((c0 (integer->char 0)) (s128 (string (integer->char 128))))
(string-append txt s128 (make-string (- 7 (modulo (string-length txt) 8)) c0))))
(define (unpad txt)
(do ((i (- (string-length txt) 1) (- i 1)))
((= (char->integer (string-ref txt i)) 128) (substring txt 0 i))))
(define (read8 port)
(let loop ((n 7) (c (read-char port)) (cs '()))
(cond ((eof-object? c) (if (null? cs) c (list->string (reverse cs))))
((zero? n) (list->string (reverse (cons c cs))))
(else (loop (- n 1) (read-char port) (cons c cs))))))
Here are functions that encipher and decipher using ECB Electronic Code Book.
(define (ecb-encipher encipher port)
(define (encode txt) (encipher (ascii->bits txt)))
(let loop ((txt (read8 port)))
(cond ((eof-object? txt) (display (bits->ascii (encode (pad "")))))
((< (string-length txt) 8) (display (bits->ascii (encode (pad txt)))))
(else (display (bits->ascii (encode txt))) (loop (read8 port))))))
(define (ecb-decipher decipher port)
(define (decode txt) (decipher (ascii->bits txt)))
(let ((txt (read8 port)))
(if (eof-object? txt) (error 'ecb-decipher "no data")
(let loop ((txt txt) (next (read8 port)))
(if (eof-object? next)
(display (unpad (bits->ascii (decode txt))))
(begin (display (bits->ascii (decode txt)))
(loop next (read8 port))))))))
Here are functions that encipher and decipher using CBC Cipher Block Chaining:
(define (cbc-encipher encipher iv port)
(define (encode prev txt) (encipher (vector-xor (ascii->bits txt) prev)))
(let loop ((prev iv) (txt (read8 port)))
(cond ((eof-object? txt) (display (bits->ascii (encode prev (pad "")))))
((< (string-length txt) 8) (display (bits->ascii (encode prev (pad txt)))))
(else (let ((next (encode prev txt)))
(display (bits->ascii next))
(loop next (read8 port)))))))
(define (cbc-decipher decipher iv port)
(define (decode prev txt) (vector-xor (decipher (ascii->bits txt)) prev))
(let loop ((prev iv) (txt (read8 port)))
(if (eof-object? (peek-char port))
(display (unpad (bits->ascii (decode prev txt))))
(let ((next (decode prev txt)))
(display (bits->ascii next))
(loop (ascii->bits txt) (read8 port))))))
Here are functions that encipher and decipher using CFB Cipher Feedback:
(define (cfb-encipher encipher iv port)
(define (encode prev txt) (vector-xor (encipher prev) (ascii->bits txt)))
(let loop ((prev iv) (txt (read8 port)))
(when (not (eof-object? txt))
(let ((len (string-length txt)))
(if (= len 8)
(let ((next (encode prev txt)))
(display (bits->ascii next))
(loop next (read8 port)))
(display (bits->ascii
(vector-slice (encode prev (pad txt)) 0 (* 8 len)))))))))
(define (cfb-decipher encipher iv port)
(define (encode prev txt) (vector-xor (encipher prev) (ascii->bits txt)))
(let loop ((prev iv) (txt (read8 port)))
(when (not (eof-object? txt))
(let ((len (string-length txt)))
(if (= len 8)
(let ((next (encode prev txt)))
(display (bits->ascii next))
(loop (ascii->bits txt) (read8 port)))
(display (bits->ascii
(vector-slice (encode prev (pad txt)) 0 (* 8 len)))))))))
Here are functions that encipher and decipher using OFB Output Feedback:
(define (ofb-encipher encipher iv port)
(do ((txt (read8 port) (read8 port))
(o (encipher iv) (encipher o)))
((eof-object? txt))
(display (bits->ascii
(if (= (string-length txt) 8)
(vector-xor (ascii->bits txt) o)
(let* ((bits (ascii->bits txt)) (len (vector-length bits)))
(vector-xor bits (vector-slice o 0 len))))))))
(define ofb-decipher ofb-encipher)
All of these functions take as their first argument a function that provides either enciphering or deciphering, as appropriate, in which the key has been partially applied. That’s because all are higher-order functions, and may be applied to various block ciphers to provide block-mode operations.
All of these functions are called in similar ways; we show an example of CBC below, using the key 0123456789ABCDEF
. Note that the initialization vector, which we give using the salt ProgPrax
, is not used for ECB:
(with-input-from-file "des-cbc.plain"
(lambda ()
(with-output-to-file "des-cbc.cipher"
(lambda ()
(cbc-encipher
(lambda (block)
(encipher
(key-schedule (hex->bits "0123456789ABCDEF"))
block))
(ascii->bits "ProgPrax")
(current-input-port))))))
(with-input-from-file "des-cbc.cipher"
(lambda ()
(with-output-to-file "des-cbc.plain.2"
(lambda ()
(cbc-decipher
(lambda (block)
(decipher
(key-schedule (hex->bits "0123456789ABCDEF"))
block))
(ascii->bits "ProgPrax")
(current-input-port))))))
You can see all the code at http://programmingpraxis.codepad.org/lNLCu1vS.