; -*- mode: lisp -*-
(defpackage #:ironclad-system
  (:use :cl :asdf))

(in-package #:ironclad-system)

;;; easy-to-type readmacro for creating s-boxes and the like

(defun array-reader (stream subchar arg)
  (declare (ignore subchar))
  (let ((array-data (read stream nil stream nil))
        (array-element-type `(unsigned-byte ,arg)))
    ;; FIXME: need to make this work for multi-dimensional arrays
    `(make-array ,(length array-data) :element-type ',array-element-type
                :initial-contents ',array-data)))
    
(defparameter *ironclad-readtable*
  (let ((readtable (copy-readtable nil)))
    (set-dispatch-macro-character #\# #\@ #'array-reader readtable)
    readtable))

(defclass ironclad-source-file (cl-source-file) ())

(defsystem :ironclad
  :version "0.13"
  :author "Nathan Froyd <froydnj@cs.rice.edu>"
  :description "A cryptographic toolkit written in pure Common Lisp"
  :default-component-class ironclad-source-file
  #+sbcl :depends-on #+sbcl (sb-rotate-byte)
  :components ((:static-file "README")
               (:static-file "LICENSE")
               (:static-file "TODO")
               (:static-file "NEWS")
               (:file "package")
               (:file "conditions" :depends-on ("package"))
               (:file "util" :depends-on ("package"))
               (:file "cipher" :depends-on ("package"))
               (:file "digest" :depends-on ("package" "common"))
               (:file "common" :depends-on ("package"))
               #+(or sbcl openmcl cmu allegro) (:file "octet-stream" :depends-on ("common"))
               (:file "modes" :depends-on ("cipher"))
               ;; public-key cryptography
               (:file "public-key" :depends-on ("package"))
               (:file "dsa" :depends-on ("public-key" "package"))
               ;; hash functions
               (:file "crc24" :depends-on ("common" "digest"))
               (:file "crc32" :depends-on ("common" "digest"))
               (:file "adler32" :depends-on ("common" "digest"))
               (:file "md2" :depends-on ("common" "digest"))
	       (:file "md4" :depends-on ("common" "digest"))
	       (:file "md5" :depends-on ("common" "digest"))
	       (:file "sha1" :depends-on ("common" "digest"))
               (:file "sha256" :depends-on ("common" "digest"))
	       (:file "ripemd-128" :depends-on ("common" "digest"))
	       (:file "ripemd-160" :depends-on ("common" "digest"))
	       (:file "tiger" :depends-on ("common" "digest"))
               (:file "hmac" :depends-on ("common" "digest"))
               ;; block ciphers of various kinds
               (:file "aes" :depends-on ("common" "modes" "cipher"))
               (:file "des" :depends-on ("common" "modes" "cipher"))
               (:file "blowfish" :depends-on ("common" "modes" "cipher"))
               (:file "twofish" :depends-on ("common" "modes" "cipher"))
               (:file "idea" :depends-on ("common" "modes" "cipher"))
               (:file "misty1" :depends-on ("common" "modes" "cipher"))
               (:file "square" :depends-on ("common" "modes" "cipher"))
               (:file "rc2" :depends-on ("common" "modes" "cipher"))
               (:file "rc5" :depends-on ("common" "modes" "cipher"))
               (:file "rc6" :depends-on ("common" "modes" "cipher"))
               (:file "tea" :depends-on ("common" "modes" "cipher"))
               (:file "xtea" :depends-on ("common" "modes" "cipher"))
               (:file "cast5" :depends-on ("common" "modes" "cipher"))
               ;; stream ciphers
               (:file "arcfour" :depends-on ("common" "modes" "cipher"))))

(defmethod perform :around ((op compile-op) (c ironclad-source-file))
  (let ((*readtable* *ironclad-readtable*)
        (*print-base* 10)               ; INTERN'ing FORMAT'd symbols
        #+sbcl (sb-ext:*inline-expansion-limit* (max sb-ext:*inline-expansion-limit* 1000))
        #+sbcl (*features* (cons sb-c:*backend-byte-order* *features*))
        #+cmu (ext:*inline-expansion-limit* (max ext:*inline-expansion-limit* 1000))
        #+cmu (*features* (cons (c:backend-byte-order c:*target-backend*) *features*)))
    (call-next-method)))

(defmethod perform :after ((op load-op) (c (eql (find-system :ironclad))))
  (provide 'ironclad))


;;; testing

(defclass test-vector-file (cl-source-file)
  ((algorithm :initarg :algorithm :reader algorithm)))
(defclass cipher-test-vector-file (test-vector-file) ())
(defclass digest-test-vector-file (test-vector-file) ())

(defpackage :ironclad-tests
  (:nicknames :crypto-tests)
  (:use :cl))

(defvar ironclad-tests::*test-number* nil "The test number being compiled.")
(defvar ironclad-tests::*test-cipher* nil "The cipher currently being tested.")
(defvar ironclad-tests::*test-digest* nil "The digest currently being tested.")

(defmethod perform :around ((op compile-op) (c cipher-test-vector-file))
  (let ((ironclad-tests::*test-cipher* (algorithm c))
        (ironclad-tests::*test-number* 0))
    (call-next-method)))
(defmethod perform :around ((op compile-op) (c digest-test-vector-file))
  (let ((ironclad-tests::*test-digest* (algorithm c))
        (ironclad-tests::*test-number* 0))
    (call-next-method)))

(defmethod perform ((op test-op) (c (eql (find-system :ironclad))))
  (oos 'load-op 'ironclad-tests)
  (oos 'test-op 'ironclad-tests))

(defsystem ironclad-tests
  :depends-on (ironclad)
  :version "0.3"
  :components ((:file "rt")
               (:file "testfuns" :depends-on ("rt"))
               (:file "hmac" :depends-on ("testfuns")
                      :pathname #.(make-pathname :directory '(:relative "test-vectors")))
               (:file "modes" :depends-on ("testfuns")
                      :pathname #.(make-pathname :directory '(:relative "test-vectors")))
               (:module "cipher-tests"
                        :depends-on ("testfuns")
                        :default-component-class cipher-test-vector-file
                        :components
                        ((:file "aes-test-vectors" :algorithm :aes)
                         (:file "blowfish-test-vectors" :algorithm :blowfish)
                         (:file "cast5-test-vectors" :algorithm :cast5)
                         (:file "des-test-vectors" :algorithm :des)
                         (:file "3des-test-vectors" :algorithm :3des)
                         (:file "misty1-test-vectors" :algorithm :misty1)
                         (:file "rc2-test-vectors" :algorithm :rc2)
                         (:file "rc5-test-vectors" :algorithm :rc5)
                         (:file "rc6-test-vectors" :algorithm :rc6)
                         (:file "idea-test-vectors" :algorithm :idea)
                         (:file "twofish-test-vectors" :algorithm :twofish)

                         (:file "arcfour-test-vectors" :algorithm :arcfour))
                        :pathname #.(make-pathname :directory '(:relative "test-vectors")))
               (:module "digest-tests"
                        :depends-on ("testfuns")
                        :default-component-class digest-test-vector-file
                        :components
                        ((:file "md2-test-vectors" :algorithm :md2)
                         (:file "md4-test-vectors" :algorithm :md4)
                         (:file "md5-test-vectors" :algorithm :md5)
                         (:file "sha1-test-vectors" :algorithm :sha1)
                         (:file "sha256-test-vectors" :algorithm :sha256)
                         (:file "ripemd-128-test-vectors" :algorithm :ripemd-128)
                         (:file "ripemd-160-test-vectors" :algorithm :ripemd-160)
                         (:file "tiger-test-vectors" :algorithm :tiger))
                        :pathname #.(make-pathname :directory '(:relative "test-vectors")))))

(defmethod perform ((op test-op) (c (eql (find-system :ironclad-tests))))
  (or (funcall (intern "DO-TESTS" (find-package "RT")))
      (error "TEST-OP failed for IRONCLAD-TESTS")))
