Coverage report: /home/ellis/comp/ext/ironclad/src/ciphers/chacha.lisp

KindCoveredAll%
expression0221 0.0
branch014 0.0
Key
Not instrumented
Conditionalized out
Executed
Not executed
 
Both branches taken
One branch taken
Neither branch taken
1
 ;;; chacha.lisp --implementation of the ChaCha stream cipher
2
 (in-package :crypto)
3
 
4
 (declaim (type (simple-octet-vector 16) chacha-sigma chacha-tau))
5
 (defconst chacha-sigma
6
   #.(coerce (map 'vector #'char-code "expand 32-byte k") 'simple-octet-vector))
7
 
8
 (defconst chacha-tau
9
   #.(coerce (map 'vector #'char-code "expand 16-byte k") 'simple-octet-vector))
10
 
11
 (deftype chacha-state () '(simple-array (unsigned-byte 32) (16)))
12
 (deftype chacha-keystream-buffer () '(simple-octet-vector 64))
13
 
14
 (declaim (inline chacha-core))
15
 (defun chacha-core (n-rounds buffer state)
16
   (declare (type chacha-keystream-buffer buffer))
17
   (declare (type chacha-state state))
18
   (declare (optimize speed))
19
   #+(and x86-64 ironclad-assembly)
20
   (x-chacha-core n-rounds buffer state)
21
   #-(and sbcl x86-64 ironclad-assembly)
22
   (let ((x (make-array 16 :element-type '(unsigned-byte 32))))
23
     (declare (dynamic-extent x))
24
     (replace x state)
25
     (macrolet ((combine (x y shift)
26
                  `(rol32 (logxor ,x ,y) ,shift))
27
                (ref (i)
28
                  `(aref x ,i))
29
                (quarter-round (y0 y1 y2 y3)
30
                  `(setf (ref ,y0) (mod32+ (ref ,y0) (ref ,y1))
31
                         (ref ,y3) (combine (ref ,y3) (ref ,y0) 16)
32
                         (ref ,y2) (mod32+ (ref ,y2) (ref ,y3))
33
                         (ref ,y1) (combine (ref ,y1) (ref ,y2) 12)
34
                         (ref ,y0) (mod32+ (ref ,y0) (ref ,y1))
35
                         (ref ,y3) (combine (ref ,y3) (ref ,y0) 8)
36
                         (ref ,y2) (mod32+ (ref ,y2) (ref ,y3))
37
                         (ref ,y1) (combine (ref ,y1) (ref ,y2) 7))))
38
       (dotimes (i n-rounds)
39
         (quarter-round 0 4 8 12)
40
         (quarter-round 1 5 9 13)
41
         (quarter-round 2 6 10 14)
42
         (quarter-round 3 7 11 15)
43
 
44
         (quarter-round 0 5 10 15)
45
         (quarter-round 1 6 11 12)
46
         (quarter-round 2 7 8 13)
47
         (quarter-round 3 4 9 14))
48
       (dotimes (i 16)
49
         (setf (ub32ref/le buffer (* i 4))
50
               (mod32+ (aref x i) (aref state i))))))
51
   (values))
52
 
53
 (defun chacha/8-core (buffer state)
54
   (declare (type chacha-keystream-buffer buffer))
55
   (declare (type chacha-state state))
56
   (chacha-core 4 buffer state))
57
 
58
 (defun chacha/12-core (buffer state)
59
   (declare (type chacha-keystream-buffer buffer))
60
   (declare (type chacha-state state))
61
   (chacha-core 6 buffer state))
62
 
63
 (defun chacha/20-core (buffer state)
64
   (declare (type chacha-keystream-buffer buffer))
65
   (declare (type chacha-state state))
66
   (chacha-core 10 buffer state))
67
 
68
 (defclass chacha (stream-cipher)
69
   ((state :reader chacha-state
70
           :initform (make-array 16 :element-type '(unsigned-byte 32)
71
                                 :initial-element 0)
72
           :type chacha-state)
73
    (counter-size :accessor chacha-counter-size
74
                  :initform 2
75
                  :type (integer 1 2))
76
    (keystream-buffer :reader chacha-keystream-buffer
77
                      :initform (make-array 64 :element-type '(unsigned-byte 8))
78
                      :type chacha-keystream-buffer)
79
    (keystream-buffer-remaining :accessor chacha-keystream-buffer-remaining
80
                                :initform 0
81
                                :type (integer 0 64))
82
    (core-function :reader chacha-core-function
83
                   :initarg :core-function
84
                   :type function))
85
   (:default-initargs :core-function #'chacha/20-core))
86
 
87
 (defclass chacha/12 (chacha)
88
   ()
89
   (:default-initargs :core-function #'chacha/12-core))
90
 
91
 (defclass chacha/8 (chacha)
92
   ()
93
   (:default-initargs :core-function #'chacha/8-core))
94
 
95
 (defun chacha-keyify (cipher key)
96
   (declare (type chacha cipher))
97
   (let ((state (chacha-state cipher)))
98
     (declare (type chacha-state state))
99
     (multiple-value-bind (constants offset)
100
         (if (= (length key) 16)
101
             (values chacha-tau 0)
102
             (values chacha-sigma 16))
103
       (setf (aref state 4) (ub32ref/le key 0)
104
             (aref state 5) (ub32ref/le key 4)
105
             (aref state 6) (ub32ref/le key 8)
106
             (aref state 7) (ub32ref/le key 12))
107
       (setf (aref state 8) (ub32ref/le key (+ offset 0))
108
             (aref state 9) (ub32ref/le key (+ offset 4))
109
             (aref state 10) (ub32ref/le key (+ offset 8))
110
             (aref state 11) (ub32ref/le key (+ offset 12)))
111
       (setf (aref state 0) (ub32ref/le constants 0)
112
             (aref state 1) (ub32ref/le constants 4)
113
             (aref state 2) (ub32ref/le constants 8)
114
             (aref state 3) (ub32ref/le constants 12))
115
       (values))))
116
 
117
 (defmethod shared-initialize :after ((cipher chacha) slot-names
118
                                      &rest initargs
119
                                      &key (key nil key-p)
120
                                      (initialization-vector nil iv-p)
121
                                      &allow-other-keys)
122
   (declare (ignore initargs key key-p iv-p))
123
   (setf (chacha-keystream-buffer-remaining cipher) 0)
124
   (when initialization-vector
125
     (when (< (length initialization-vector) 8)
126
       (error 'invalid-initialization-vector
127
              :cipher (class-name (class-of cipher))
128
              :block-length 8))
129
     (let ((state (chacha-state cipher)))
130
       (declare (type chacha-state state))
131
       (case (length initialization-vector)
132
         ((12)
133
          ;; 32-bit counter and 96-bit nonce of the RFC 8439 variant
134
          (setf (chacha-counter-size cipher) 1)
135
          (setf (aref state 12) 0
136
                (aref state 13) (ub32ref/le initialization-vector 0)
137
                (aref state 14) (ub32ref/le initialization-vector 4)
138
                (aref state 15) (ub32ref/le initialization-vector 8)))
139
         (t
140
          ;; 64-bit counter and 64-bit nonce of the original algorithm
141
          (setf (chacha-counter-size cipher) 2)
142
          (setf (aref state 12) 0
143
                (aref state 13) 0
144
                (aref state 14) (ub32ref/le initialization-vector 0)
145
                (aref state 15) (ub32ref/le initialization-vector 4))))))
146
   cipher)
147
 
148
 (defmethod schedule-key ((cipher chacha) key)
149
   (chacha-keyify cipher key)
150
   cipher)
151
 
152
 (define-stream-cryptor chacha
153
   (let ((state (chacha-state context))
154
         (keystream-buffer (chacha-keystream-buffer context))
155
         (keystream-buffer-remaining (chacha-keystream-buffer-remaining context))
156
         (core-function (chacha-core-function context)))
157
     (declare (type chacha-state state)
158
              (type chacha-keystream-buffer keystream-buffer)
159
              (type (integer 0 64) keystream-buffer-remaining)
160
              (type function core-function))
161
     (unless (zerop length)
162
       (unless (zerop keystream-buffer-remaining)
163
         (let ((size (min length keystream-buffer-remaining)))
164
           (declare (type (integer 0 64) size))
165
           (xor-block size keystream-buffer (- 64 keystream-buffer-remaining)
166
                      plaintext plaintext-start
167
                      ciphertext ciphertext-start)
168
           (decf keystream-buffer-remaining size)
169
           (decf length size)
170
           (incf ciphertext-start size)
171
           (incf plaintext-start size)))
172
       (unless (zerop length)
173
         (loop
174
           (funcall core-function keystream-buffer state)
175
           (when (zerop (setf (aref state 12)
176
                              (mod32+ (aref state 12) 1)))
177
             (setf (aref state 13) (mod32+ (aref state 13) 1)))
178
           (when (<= length 64)
179
             (xor-block length keystream-buffer 0 plaintext plaintext-start
180
                        ciphertext ciphertext-start)
181
             (setf (chacha-keystream-buffer-remaining context) (- 64 length))
182
             (return-from chacha-crypt (values)))
183
           (xor-block 64 keystream-buffer 0 plaintext plaintext-start
184
                      ciphertext ciphertext-start)
185
           (decf length 64)
186
           (incf ciphertext-start 64)
187
           (incf plaintext-start 64)))
188
       (setf (chacha-keystream-buffer-remaining context) keystream-buffer-remaining))
189
     (values)))
190
 
191
 (defcipher chacha
192
   (:mode :stream)
193
   (:crypt-function chacha-crypt)
194
   (:key-length (:fixed 16 32)))
195
 
196
 (defcipher chacha/12
197
   (:mode :stream)
198
   (:crypt-function chacha-crypt)
199
   (:key-length (:fixed 16 32)))
200
 
201
 (defcipher chacha/8
202
   (:mode :stream)
203
   (:crypt-function chacha-crypt)
204
   (:key-length (:fixed 16 32)))