xref: /dragonfly/sys/dev/crypto/padlock/padlock_cipher.c (revision 7d596a54d65954e7b1fe23694d5a3ae5db50d023)
1 /*-
2  * Copyright (c) 2005-2006 Pawel Jakub Dawidek <pjd@FreeBSD.org>
3  * Copyright (c) 2004 Mark R V Murray
4  * All rights reserved.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  * 1. Redistributions of source code must retain the above copyright
10  *    notice, this list of conditions and the following disclaimer.
11  * 2. Redistributions in binary form must reproduce the above copyright
12  *    notice, this list of conditions and the following disclaimer in the
13  *    documentation and/or other materials provided with the distribution.
14  *
15  * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND
16  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE
19  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25  * SUCH DAMAGE.
26  */
27 
28 /*        $OpenBSD: via.c,v 1.3 2004/06/15 23:36:55 deraadt Exp $     */
29 /*        $FreeBSD: src/sys/crypto/via/padlock_cipher.c,v 1.5 2006/09/15 10:44:55 pjd Exp $ */
30 /*-
31  * Copyright (c) 2003 Jason Wright
32  * Copyright (c) 2003, 2004 Theo de Raadt
33  * All rights reserved.
34  *
35  * Permission to use, copy, modify, and distribute this software for any
36  * purpose with or without fee is hereby granted, provided that the above
37  * copyright notice and this permission notice appear in all copies.
38  *
39  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
40  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
41  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
42  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
43  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
44  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
45  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
46  */
47 
48 #include <sys/param.h>
49 #include <sys/systm.h>
50 #include <sys/kernel.h>
51 #include <sys/module.h>
52 #include <sys/malloc.h>
53 #include <sys/libkern.h>
54 #include <sys/uio.h>
55 
56 #include <opencrypto/cryptodev.h>
57 #include <crypto/rijndael/rijndael.h>
58 
59 #include <dev/crypto/padlock/padlock.h>
60 
61 #define   PADLOCK_ROUND_COUNT_AES128    10
62 #define   PADLOCK_ROUND_COUNT_AES192    12
63 #define   PADLOCK_ROUND_COUNT_AES256    14
64 
65 #define   PADLOCK_ALGORITHM_TYPE_AES    0
66 
67 #define   PADLOCK_KEY_GENERATION_HW     0
68 #define   PADLOCK_KEY_GENERATION_SW     1
69 
70 #define   PADLOCK_DIRECTION_ENCRYPT     0
71 #define   PADLOCK_DIRECTION_DECRYPT     1
72 
73 #define   PADLOCK_KEY_SIZE_128          0
74 #define   PADLOCK_KEY_SIZE_192          1
75 #define   PADLOCK_KEY_SIZE_256          2
76 
77 MALLOC_DECLARE(M_PADLOCK);
78 
79 static __inline void
padlock_cbc(void * in,void * out,size_t count,void * key,union padlock_cw * cw,void * iv)80 padlock_cbc(void *in, void *out, size_t count, void *key, union padlock_cw *cw,
81     void *iv)
82 {
83           /* The .byte line is really VIA C3 "xcrypt-cbc" instruction */
84           __asm __volatile(
85                     "pushf                                  \n\t"
86                     "popf                                   \n\t"
87                     "rep                                    \n\t"
88                     ".byte    0x0f, 0xa7, 0xd0"
89                               : "+a" (iv), "+c" (count), "+D" (out), "+S" (in)
90                               : "b" (key), "d" (cw)
91                               : "cc", "memory"
92                     );
93 }
94 
95 static void
padlock_cipher_key_setup(struct padlock_session * ses,caddr_t key,int klen)96 padlock_cipher_key_setup(struct padlock_session *ses, caddr_t key, int klen)
97 {
98           union padlock_cw *cw;
99           int i;
100 
101           cw = &ses->ses_cw;
102           if (cw->cw_key_generation == PADLOCK_KEY_GENERATION_SW) {
103                     /* Build expanded keys for both directions */
104                     rijndaelKeySetupEnc(ses->ses_ekey, key, klen);
105                     rijndaelKeySetupDec(ses->ses_dkey, key, klen);
106                     for (i = 0; i < 4 * (RIJNDAEL_MAXNR + 1); i++) {
107                               ses->ses_ekey[i] = ntohl(ses->ses_ekey[i]);
108                               ses->ses_dkey[i] = ntohl(ses->ses_dkey[i]);
109                     }
110           } else {
111                     bcopy(key, ses->ses_ekey, klen);
112                     bcopy(key, ses->ses_dkey, klen);
113           }
114 }
115 
116 int
padlock_cipher_setup(struct padlock_session * ses,struct cryptoini * encini)117 padlock_cipher_setup(struct padlock_session *ses, struct cryptoini *encini)
118 {
119           union padlock_cw *cw;
120 
121           if (encini->cri_klen != 128 && encini->cri_klen != 192 &&
122               encini->cri_klen != 256) {
123                     return (EINVAL);
124           }
125 
126           cw = &ses->ses_cw;
127           bzero(cw, sizeof(*cw));
128           cw->cw_algorithm_type = PADLOCK_ALGORITHM_TYPE_AES;
129           cw->cw_key_generation = PADLOCK_KEY_GENERATION_SW;
130           cw->cw_intermediate = 0;
131           switch (encini->cri_klen) {
132           case 128:
133                     cw->cw_round_count = PADLOCK_ROUND_COUNT_AES128;
134                     cw->cw_key_size = PADLOCK_KEY_SIZE_128;
135 #ifdef HW_KEY_GENERATION
136                     /* This doesn't buy us much, that's why it is commented out. */
137                     cw->cw_key_generation = PADLOCK_KEY_GENERATION_HW;
138 #endif
139                     break;
140           case 192:
141                     cw->cw_round_count = PADLOCK_ROUND_COUNT_AES192;
142                     cw->cw_key_size = PADLOCK_KEY_SIZE_192;
143                     break;
144           case 256:
145                     cw->cw_round_count = PADLOCK_ROUND_COUNT_AES256;
146                     cw->cw_key_size = PADLOCK_KEY_SIZE_256;
147                     break;
148           }
149           if (encini->cri_key != NULL) {
150                     padlock_cipher_key_setup(ses, encini->cri_key,
151                         encini->cri_klen);
152           }
153 
154           karc4random_buf(ses->ses_iv, sizeof(ses->ses_iv));
155           return (0);
156 }
157 
158 /*
159  * Function checks if the given buffer is already 16 bytes aligned.
160  * If it is there is no need to allocate new buffer.
161  * If it isn't, new buffer is allocated.
162  */
163 static u_char *
padlock_cipher_alloc(struct cryptodesc * enccrd,struct cryptop * crp,int * allocated)164 padlock_cipher_alloc(struct cryptodesc *enccrd, struct cryptop *crp,
165     int *allocated)
166 {
167           u_char *addr;
168 
169           if (crp->crp_flags & CRYPTO_F_IMBUF)
170                     goto alloc;
171           else {
172                     if (crp->crp_flags & CRYPTO_F_IOV) {
173                               struct uio *uio;
174                               struct iovec *iov;
175 
176                               uio = (struct uio *)crp->crp_buf;
177                               if (uio->uio_iovcnt != 1)
178                                         goto alloc;
179                               iov = uio->uio_iov;
180                               addr = (u_char *)iov->iov_base + enccrd->crd_skip;
181                     } else {
182                               addr = (u_char *)crp->crp_buf;
183                     }
184                     if (((uintptr_t)addr & 0xf) != 0) /* 16 bytes aligned? */
185                               goto alloc;
186                     *allocated = 0;
187                     return (addr);
188           }
189 alloc:
190           *allocated = 1;
191           addr = kmalloc(enccrd->crd_len + 16, M_PADLOCK, M_NOWAIT);
192           return (addr);
193 }
194 
195 int
padlock_cipher_process(struct padlock_session * ses,struct cryptodesc * enccrd,struct cryptop * crp)196 padlock_cipher_process(struct padlock_session *ses, struct cryptodesc *enccrd,
197     struct cryptop *crp)
198 {
199           union padlock_cw *cw;
200           u_char *buf, *abuf;
201           uint32_t *key;
202           int allocated;
203 
204           buf = padlock_cipher_alloc(enccrd, crp, &allocated);
205           if (buf == NULL)
206                     return (ENOMEM);
207           /* Buffer has to be 16 bytes aligned. */
208           abuf = PADLOCK_ALIGN(buf);
209 
210           if ((enccrd->crd_flags & CRD_F_KEY_EXPLICIT) != 0) {
211                     padlock_cipher_key_setup(ses, enccrd->crd_key,
212                         enccrd->crd_klen);
213           }
214 
215           cw = &ses->ses_cw;
216           cw->cw_filler0 = 0;
217           cw->cw_filler1 = 0;
218           cw->cw_filler2 = 0;
219           cw->cw_filler3 = 0;
220           if ((enccrd->crd_flags & CRD_F_ENCRYPT) != 0) {
221                     cw->cw_direction = PADLOCK_DIRECTION_ENCRYPT;
222                     key = ses->ses_ekey;
223                     if ((enccrd->crd_flags & CRD_F_IV_EXPLICIT) != 0)
224                               bcopy(enccrd->crd_iv, ses->ses_iv, AES_BLOCK_LEN);
225 
226                     if ((enccrd->crd_flags & CRD_F_IV_PRESENT) == 0) {
227                               crypto_copyback(crp->crp_flags, crp->crp_buf,
228                                   enccrd->crd_inject, AES_BLOCK_LEN, ses->ses_iv);
229                     }
230           } else {
231                     cw->cw_direction = PADLOCK_DIRECTION_DECRYPT;
232                     key = ses->ses_dkey;
233                     if ((enccrd->crd_flags & CRD_F_IV_EXPLICIT) != 0)
234                               bcopy(enccrd->crd_iv, ses->ses_iv, AES_BLOCK_LEN);
235                     else {
236                               crypto_copydata(crp->crp_flags, crp->crp_buf,
237                                   enccrd->crd_inject, AES_BLOCK_LEN, ses->ses_iv);
238                     }
239           }
240 
241           if (allocated) {
242                     crypto_copydata(crp->crp_flags, crp->crp_buf, enccrd->crd_skip,
243                         enccrd->crd_len, abuf);
244           }
245 
246           padlock_cbc(abuf, abuf, enccrd->crd_len / AES_BLOCK_LEN, key, cw,
247               ses->ses_iv);
248 
249           if (allocated) {
250                     crypto_copyback(crp->crp_flags, crp->crp_buf, enccrd->crd_skip,
251                         enccrd->crd_len, abuf);
252           }
253 
254           /* copy out last block for use as next session IV */
255           if ((enccrd->crd_flags & CRD_F_ENCRYPT) != 0) {
256                     crypto_copydata(crp->crp_flags, crp->crp_buf,
257                         enccrd->crd_skip + enccrd->crd_len - AES_BLOCK_LEN,
258                         AES_BLOCK_LEN, ses->ses_iv);
259           }
260 
261           if (allocated) {
262                     bzero(buf, enccrd->crd_len + 16);
263                     kfree(buf, M_PADLOCK);
264           }
265           return (0);
266 }
267