A Common Lisp implementation of ARC4 (trademark: RC4), a stream cipher, can be found below. Note that experts in sci.crypt do not believe that the key management proposed there is safe. As codified in RFC 7465, RC4 is no longer allowed in TLS, and major browsers have dropped support. Some cryptography packages—including Ironclad and random-state—provide implementations.

Another implementation may be found here, which discards the first 3072 bytes of the crypto-stream as recommended. It also provides a general-purpose seedable PRNG (pseudo-random number generator) as part of the API (inherently used as part of any stream-cipher implementation but not always exported). It's under a BSD license.

(in-package :cl-user) ;;; CipherSaber in Common Lisp - Daniel Barlow <> ;;; Excessively longer than 16 lines of qbasic due in part to gratuitous ;;; commenting ;;; "byte" has a perfectly good meaning in Common Lisp already, and as ;;; you'd expect for a language that grew up on weird hardware, it's ;;; not limited to 8 bits. So we say "octet" instead to avoid ;;; confusion, at some small risk of pleasing the French (eval-when (:compile-toplevel :load-toplevel) ;; the `eval-when' form makes sure this gets done before anything ;; else. In a grown-up program we'd put the deftype in its own file (deftype octet () '(integer 0 255))) (defun octet+ (&rest octets) ;; XXX fill this chock-full of declarations so it gets inlined and ;; gratuitously type-inferenced (mod (apply #'+ octets) 256)) ;;; CL style note: some people like the LOOP macro. Others don't. ;;; It's a religious point, really. ;;; (And Perl users think that they invented TMTOWTDI - fah) (defun make-state-array () "Return a 256-octet long array of octets with values 0 thru 255" (coerce (loop for i from 0 to 255 collect i) '(vector octet))) ;;; Semi-serious note: this is using your Lisp's default RNG, which ;;; may or may not be any good, and most probably is only pseudorandom ;;; anyway. You could replace it with something ;;; implementation-specific (read /dev/random, say) if you like (defun make-initialization-vector () "Return a random ten-octet initialization vector." (coerce (loop for i from 0 to 9 collect (random 256)) '(vector octet))) (defun encipher (initial-vector user-key in-stream out-stream) "CipherSaber en(de)crypt the message on IN-STREAM to OUT-STREAM. Use the provided INITIAL-VECTOR and USER-KEY" (let* ((state (make-state-array)) (key (concatenate '(vector octet) (map 'vector #'char-code user-key) initial-vector)) (key-length (length key))) ;; mix the state (loop for i from 0 to 255 for j = (octet+ (or j 0) (elt state i) (elt key (mod i key-length))) do (rotatef (elt state i) (elt state j))) ;; now do the ciphering (let ((eof (gensym))) (loop for in = (read-char in-stream nil eof) until (eq in eof) for i = 1 then (octet+ 1 i) for j = (octet+ (or j 0) (elt state i)) for n = (octet+ (elt state i) (elt state j)) do (rotatef (elt state i) (elt state j)) do (princ (code-char (logxor (elt state n) (char-code in))) out-stream))))) (defun encrypt-message (user-key in-stream out-stream) "Encrypt the message on IN-STREAM to OUT-STREAM using USER-KEY with a randomly-generated IV" (let ((iv (make-initialization-vector))) (write-sequence (map 'vector #'code-char iv) out-stream) (encipher iv user-key in-stream out-stream))) (defun decrypt-message (user-key in-stream out-stream) "Decrypt the message on IN-STREAM to OUT-STREAM, using USER-KEY and the first ten bytes of IN-STREAM as IV" (let ((iv (make-array 10 :element-type 'octet))) (read-sequence iv in-stream :end 10) (encipher iv user-key in-stream out-stream))) (defun decrypt-file (user-key pathname) "Decrypt the file at PATHNAME to *STANDARD-OUTPUT*" (with-open-file (i pathname) (decrypt-message user-key i *standard-output*)))