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.

Pages: 1 2

Leave a comment