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 <dan@telent.net>
;;; 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*)))