Component octet-stream

You are here: All Systems / ironclad / octet-stream

;;;; octet-stream.lisp -- like string-streams, but with (VECTOR (UNSIGNED-BYTE 8))

(in-package :crypto)


;;; portability definitions

#+cmu
(eval-when (:compile-toplevel :load-toplevel :execute)
  (require :gray-streams)
)


(eval-when (:compile-toplevel :load-toplevel :execute)
(defvar *binary-input-stream-class*
  (quote
   #+sbcl sb-gray:fundamental-binary-input-stream
   #+openmcl ccl:fundamental-binary-input-stream
   #+cmu ext:fundamental-binary-input-stream
   #+allegro excl:fundamental-binary-input-stream
   #-(or sbcl openmcl cmu allegro) (error "octet streams not supported in this implementation")
)
)


(defvar *binary-output-stream-class*
  (quote
   #+sbcl sb-gray:fundamental-binary-output-stream
   #+openmcl ccl:fundamental-binary-output-stream
   #+cmu ext:fundamental-binary-output-stream
   #+allegro excl:fundamental-binary-output-stream
   #-(or sbcl openmcl cmu allegro) (error "octet streams not supported in this implementation")
)
)


;;; FIXME: how to do CMUCL support for this?
(defvar *stream-element-type-function*
  (quote
   #+sbcl sb-gray::stream-element-type
   #+openmcl cl:stream-element-type
   #+cmu cl:stream-element-type
   #+allegro cl:stream-element-type
   #-(or sbcl openmcl cmu allegro) (error "octet streams not supported in this implementation")
)
)


(defvar *stream-read-byte-function*
  (quote
   #+sbcl sb-gray:stream-read-byte
   #+openmcl ccl:stream-read-byte
   #+cmu ext:stream-read-byte
   #+allegro excl:stream-read-byte
   #-(or sbcl openmcl cmu allegro) (error "octet streams not supported in this implementation")
)
)


(defvar *stream-write-byte-function*
  (quote
   #+sbcl sb-gray:stream-write-byte
   #+openmcl ccl:stream-write-byte
   #+cmu ext:stream-write-byte
   #+allegro excl:stream-write-byte
   #-(or sbcl openmcl cmu allegro) (error "octet streams not supported in this implementation")
)
)


;;; FIXME: would be nice to support STREAM-{READ,WRITE}-SEQUENCE, too.  The
;;; function name hacking is here, but the actual implementation (and
;;; possible arglist headaches) are not.
(defvar *stream-read-sequence-function*
  (quote
   #+sbcl sb-gray:stream-read-sequence
   #+openmcl ccl:stream-read-vector
   #+cmu ext:stream-read-sequence
   #+allegro excl:stream-read-sequence
   #-(or sbcl openmcl cmu allegro) (error "octet streams not supported in this implementation")
)
)


(defvar *stream-write-sequence-function*
  (quote
   #+sbcl sb-gray:stream-write-sequence
   #+openmcl ccl:stream-write-vector
   #+cmu ext:stream-write-sequence
   #+allegro excl:stream-write-sequence
   #-(or sbcl openmcl cmu allegro) (error "octet streams not supported in this implementation")
)
)

)



;;; implementation via Gray streams

;;; These could be specialized for particular implementations by hooking
;;; in directly to the "native" stream methods for the implementation.

(defclass octet-stream ()
  ((buffer :accessor buffer :initarg :buffer :type (simple-array (unsigned-byte 8) (*))))
)


;;; FIXME: how to do CMUCL support for this?
(defmethod #.*stream-element-type-function* ((stream octet-stream))
  '(unsigned-byte 8)
)



;;; input streams

(defclass octet-input-stream (octet-stream #.*binary-input-stream-class*)
  ((index :accessor index :initarg :index :type index)
   (end :accessor end :initarg :end :type index)
)
)


(defmethod #.*stream-read-byte-function* ((stream octet-input-stream))
  (let ((buffer (buffer stream))
        (index (index stream))
)

    (declare (type (simple-array (unsigned-byte 8) (*)) buffer))
    (cond
      ((>= index (end stream)) :eof)
      (t
       (setf (index stream) (1+ index))
       (aref buffer index)
)
)
)
)


(defun make-octet-input-stream (buffer &optional (start 0) end)
  "As MAKE-STRING-INPUT-STREAM, only with octets instead of characters."
  (declare (type (simple-array (unsigned-byte 8) (*)) buffer)
           (type index start)
           (type (or index null) end)
)

  (let ((end (or end (length buffer))))
    (make-instance 'octet-input-stream
                   :buffer buffer :index start :end end
)
)
)



;;; output streams

(defclass octet-output-stream (octet-stream #.*binary-output-stream-class*)
  ((index :accessor index :initform 0 :type index))
)


(defmethod #.*stream-write-byte-function* ((stream octet-output-stream) integer)
  (declare (type (unsigned-byte 8) integer))
  (let* ((buffer (buffer stream))
         (length (length buffer))
         (index (index stream))
)

    (declare (type (simple-array (unsigned-byte 8) (*)) buffer))
    (when (>= index (length buffer))
      (let ((new-buffer (make-array (* 2 length)
                                    :element-type '(unsigned-byte 8)
)
)
)

        (declare (type (simple-array (unsigned-byte 8) (*)) new-buffer))
        (replace new-buffer buffer)
        (setf buffer new-buffer
              (buffer stream) new-buffer
)
)
)

    (setf (aref buffer index) integer
          (index stream) (1+ index)
)

    integer
)
)


(defun get-output-stream-octets (stream)
  "As GET-OUTPUT-STREAM-STRING, only with an octet output-stream instead
of a string output-stream."

  (let ((buffer (buffer stream))
        (index (index stream))
)

    (setf (index stream) 0)
    (subseq buffer 0 index)
)
)


(defun make-octet-output-stream ()
  "As MAKE-STRING-OUTPUT-STREAM, only with octets instead of characters."
  (make-instance 'octet-output-stream
                 :buffer (make-array 128 :element-type '(unsigned-byte 8))
)
)



;;; digesting streams

(defclass digesting-stream (#.*binary-output-stream-class*)
  ((digest :initarg :digest :reader stream-digest)
   (buffer :initform (make-array 64 :element-type '(unsigned-byte 8))
           :reader stream-buffer
)

   (position :initform 0
             :reader stream-buffer-position
)
)
)


(defmethod #.*stream-element-type-function* ((stream digesting-stream))
  '(unsigned-byte 8)
)


(defun make-digesting-stream (digest)
  (make-instance 'digesting-stream :digest (make-digest digest))
)


(defmethod #.*stream-write-byte-function* ((stream digesting-stream) byte)
  (declare (type (unsigned-byte 8) byte))
  (with-slots (digest buffer position) stream
    (setf (aref buffer position) byte)
    (when (= (incf position) 64)
      (update-digest digest buffer :start 0 :end 64)
      (setf position 0)
)

    byte
)
)


(defmethod produce-digest ((stream digesting-stream)
                           &key digest (digest-start 0)
)

  (with-slots ((%digest digest) buffer position) stream
    (unless (zerop position)
      (update-digest %digest buffer :start 0 :end position)
      (setf position 0)
)

    (let ((copy (copy-digest %digest)))
      (produce-digest copy :digest digest :digest-start digest-start)
)
)
)



;;; encrypting and decrypting streams

(defclass crypting-stream ()
  ((cipher :initarg :cipher :reader stream-cipher)
   (buffer :initarg :buffer :reader stream-buffer)
   (buffer-length :initarg :buffer-length :reader stream-buffer-length)
   (position :initform 0 :reader stream-buffer-position)
   (padding :initarg :padding :reader stream-padding)
   (wrapped-stream :initarg :stream :reader stream-wrapped-stream)
)
)


(defmethod #.*stream-element-type-function* ((stream crypting-stream))
  '(unsigned-byte 8)
)


(defclass encrypting-input-stream (crypting-stream #.*binary-input-stream-class*) ())
(defclass encrypting-output-stream (crypting-stream #.*binary-output-stream-class*) ())
(defclass decrypting-input-stream (crypting-stream #.*binary-input-stream-class*) ())
(defclass decrypting-output-stream (crypting-stream #.*binary-output-stream-class*) ())

(deftype stream-direction () '(member :input :output))

(defun make-encrypting-stream (stream cipher mode key
                               &key initialization-vector
                               direction (padding :none)
)

  (declare (type stream-direction direction))
  (declare (ignorable padding))
  (let* ((context (make-cipher cipher mode key initialization-vector))
         (block-length (max (block-length cipher) 16))
         (buffer (make-array block-length :element-type '(unsigned-byte 8)))
)

    (if (eq direction :input)
        (make-instance 'encrypting-input-stream :stream stream
                       :cipher context :buffer buffer
                       :buffer-length block-length
)

        (make-instance 'encrypting-output-stream :stream stream
                       :cipher context :buffer buffer
                       :buffer-length block-length
)
)
)
)


(defun make-decrypting-stream (stream cipher mode key
                               &key initialization-vector (padding :none)
                               (direction :input)
)

  (declare (type stream-direction direction))
  (declare (ignorable padding))
  (let* ((context (make-cipher cipher mode key initialization-vector))
         (block-length (max (block-length cipher) 16))
         (buffer (make-array block-length :element-type '(unsigned-byte 8)))
)

    (if (eq direction :input)
        (make-instance 'decrypting-input-stream :stream stream
                       :cipher context :buffer buffer
                       :buffer-length block-length
)

        (make-instance 'decrypting-output-stream :stream stream
                       :cipher context :buffer buffer
                       :buffer-length block-length
)
)
)
)


;;; FIXME: padding issues
(defun crypting-stream-read-byte (stream cryptfun)
  (with-slots (wrapped-stream cipher buffer buffer-length buffer-position)
      stream
    (when (= buffer-position buffer-length)
      (read-sequence buffer wrapped-stream)
      (funcall cryptfun cipher buffer buffer)
      (setf buffer-position 0)
)

    (prog1 (aref buffer buffer-position)
      (incf buffer-position)
)
)
)


(defun crypting-stream-write-byte (stream byte cryptfun)
  (with-slots (wrapped-stream cipher buffer buffer-length buffer-position)
      stream
    (when (= buffer-position buffer-length)
      (funcall cryptfun cipher buffer buffer)
      (write-sequence buffer wrapped-stream)
      (setf buffer-position 0)
)

    (prog1 (setf (aref buffer buffer-position) byte)
      (incf buffer-position)
)
)
)


(defmethod #.*stream-read-byte-function* ((stream encrypting-input-stream))
  (crypting-stream-read-byte stream #'encrypt)
)


(defmethod #.*stream-read-byte-function* ((stream decrypting-input-stream))
  (crypting-stream-read-byte stream #'decrypt)
)


(defmethod #.*stream-write-byte-function* ((stream encrypting-output-stream) byte)
  (declare (type (unsigned-byte 8) byte))
  (crypting-stream-write-byte stream byte #'encrypt)
)


(defmethod #.*stream-write-byte-function* ((stream decrypting-output-stream) byte)
  (declare (type (unsigned-byte 8) byte))
  (crypting-stream-write-byte stream byte #'decrypt)
)

Lisppaste pastes can be made by anyone at any time. Imagine a fearsomely comprehensive disclaimer of liability. Now fear, comprehensively.