Arcfour and CipherSaber in Emacs Lisp

I have previously talked about arcfour and CipherSaber and provided implementations in C. For fun, I made an implementation of arcfour in Emacs lisp (elisp), and built upon that to make a CipherSaber implementation in elisp. Check it out with Git,

git clone git://github.com/skeeto/arcfour.git

If you don't have Git (yet), you can follow that link to use the web interface. The relevant files are arcfour.el and ciphersaber.el. There are some test vectors in there that I won't show here. Here is the arcfour implementation,

(defun rc4-init-state ()
  "Initialize the arcfour state vector."
  (interactive)
  (setq rc4-state (make-vector 256 0))
  (setq rc4-i 0)
  (setq rc4-j 0)
  (let (i)
    (dotimes (i 256 rc4-state)
      (aset rc4-state i i))))

(defun rc4-swap (i j)
  "Swap two elements in the state vector."
  (let ((temp (aref rc4-state i)))
    (aset rc4-state i (aref rc4-state j))
    (aset rc4-state j temp)))

(defun rc4-key-sched (key)
  "Arcfour key-scheduler: initialize state from key."
  (interactive "sEnter key: ")
  (let ((j 0) i)
    (dotimes (i 256 rc4-state)
      (setq j (% (+ j
                    (aref rc4-state i)
                    (aref key (% i (length key)))) 256))
      (rc4-swap i j))))

(defun rc4-gen-byte ()
  "Generate a single byte."
  (interactive)
  (setq rc4-i (% (1+ rc4-i) 256))
  (setq rc4-j (% (+ rc4-j (aref rc4-state rc4-i)) 256))
  (rc4-swap rc4-i rc4-j)
  (aref rc4-state (% (+ (aref rc4-state rc4-i)
                        (aref rc4-state rc4-j)) 256)))

For the sake of simplicity it uses some global variables to store the cipher state. It would be better if the state was returned as a list, continuation, or closure. That way we could run a bunch of different ciphers at the same time.

And this provides interactive functions so that they can be called by a user right on the buffer being used, with M-x rc4-buffer.

(defun rc4-region (start end key)
  "Encrypt/decrypt region with arcfour using given key."
  (interactive "r\nsEnter key: ")
  (rc4-init-state)
  (rc4-key-sched key)
  (save-excursion
    (let (c)
      (goto-char start)
      (while (< (point) end)
        (setq c (char-after))
        (delete-char 1)
        (insert-char (logxor c (rc4-gen-byte)) 1)))))

(defun rc4-buffer (key)
  "Encrypt/decrypt entire buffer with arcfour."
  (interactive "sEnter key: ")
  (rc4-region (point-min) (point-max) key))

You may run into encoding issues with encrypted regions. The CipherSaber implementation below gets around this problem by turning off multi-byte encoding with set-buffer-multibyte.

In ciphersaber.el we apply these functions. The CipherSaber functions can be called with M-x cs-encrypt-buffer and M-x cs-decrypt-buffer. Note that this is only CipherSaber-1, and I leave CipherSaber-2 as an exercise for the reader :-P.

(defun cs-encrypt-buffer (key)
  "Encrypt buffer with CipherSaber-1."
  (interactive "sEnter key: ")
  (set-buffer-multibyte nil)
  (let* ((iv (cs-generate-iv))
         (cs-key (concat key iv)))
    (rc4-buffer cs-key)
    (beginning-of-buffer)
    (insert iv)))

(defun cs-decrypt-buffer (key)
  "Decrypt buffer with CipherSaber-1."
  (interactive "sEnter key: ")
  (let* ((iv (cs-retrieve-iv))
         (cs-key (concat key iv)))
    (rc4-buffer cs-key)))

(defun cs-generate-iv ()
  "Generate a 10-byte initialization vector."
  (let ((iv "") i)
    (dotimes (i 10 iv)
      (setq iv (concat iv (char-to-string (random 256)))))))

(defun cs-retrieve-iv ()
  "Remove initialization vector from buffer and return it."
  (beginning-of-buffer)
  (let ((iv "") i)
    (dotimes (i 10 iv)
      (setq iv (concat iv (char-to-string (char-after))))
      (delete-char 1))))

I didn't bother with save-excursion because I didn't think it would be important where the point is in the middle of an encrypted file. Feel free to add it, though!

These functions could be used to make a minor mode to transparently encrypt and decrypt CipherSaber files. It could also be modified to take advantage of Emacs' batch mode to handle CipherSaber processing right from the shell. But those are other projects!

Have a comment on this article? Start a discussion in my public inbox by sending an email to ~skeeto/public-inbox@lists.sr.ht [mailing list etiquette] , or see existing discussions.

This post has archived comments.

null program

Chris Wellons

wellons@nullprogram.com (PGP)
~skeeto/public-inbox@lists.sr.ht (view)