mark down README
[pam_pcsc_cr.git] / authobj.c
1 /*
2 Copyright (c) 2013 Eugene Crosser
3
4 This software is provided 'as-is', without any express or implied
5 warranty. In no event will the authors be held liable for any damages
6 arising from the use of this software.
7
8 Permission is granted to anyone to use this software for any purpose,
9 including commercial applications, and to alter it and redistribute it
10 freely, subject to the following restrictions:
11
12     1. The origin of this software must not be misrepresented; you must
13     not claim that you wrote the original software. If you use this
14     software in a product, an acknowledgment in the product documentation
15     would be appreciated but is not required.
16
17     2. Altered source versions must be plainly marked as such, and must
18     not be misrepresented as being the original software.
19
20     3. This notice may not be removed or altered from any source
21     distribution.
22 */
23
24 #ifdef HAVE_CONFIG_H
25 # include "config.h"
26 #endif
27 #include <stdio.h>
28 #include <string.h>
29 #include <stdlib.h>
30 #include <alloca.h>
31 #include "serial.h"
32 #include "crypto.h"
33 #include "authobj.h"
34
35 static struct _auth_chunk
36 make_challenge(const char *uid, const char *pass, const char *nonce)
37 {
38         struct _auth_chunk ho = {0};
39         unsigned long rc;
40         serializer_t srl;
41         int datasize = strlen(uid) + strlen(pass) + strlen(nonce) +
42                         4 * sizeof(short);
43         unsigned char *data = alloca(datasize);
44         int hashsize = sizeof(ho.data);
45
46         serial_init(&srl, data, datasize);
47         if (serial_put(&srl, uid, strlen(uid)) != strlen(uid)) {
48                 ho.err = "challenge: serialization of uid failed";
49         } else if (serial_put(&srl, pass, strlen(pass)) != strlen(pass)) {
50                 ho.err = "challenge: serialization of pass failed";
51         } else if (serial_put(&srl, nonce, strlen(nonce)) != strlen(nonce)) {
52                 ho.err = "challenge: serialization of nonce failed";
53         } else if (serial_put(&srl, NULL, 0) != 0) {
54                 ho.err = "challenge: serialization of terminator failed";
55         }
56         if (!ho.err) {
57                 if ((rc = hash(data, serial_size(&srl), &ho.data, &hashsize))) {
58                         ho.err = crypto_errstr(rc);
59                 } else if (hashsize != sizeof(ho.data)) {
60                         ho.err = "challenge: hash size is wrong";
61                 }
62         }
63         memset(data, 0, datasize);
64         return ho;
65 }
66
67 static struct _auth_chunk
68 new_key(const unsigned char *challenge, const int challengesize,
69         const unsigned char *secret, const int secsize)
70 {
71         struct _auth_chunk ho = {0};
72         unsigned long rc;
73         int keysize = sizeof(ho.data);
74
75         if ((rc = hmac(secret, secsize, challenge, challengesize,
76                         &ho.data, &keysize))) {
77                 ho.err = crypto_errstr(rc);
78         } else if (keysize != sizeof(ho.data)) {
79                 ho.err = "make_key: hash size is wrong";
80         }
81         return ho;
82 }
83
84 static struct _auth_chunk
85 make_key(const char *userid, const char *password, const char *nonce,
86         const unsigned char *secret, const int secsize,
87         struct _auth_chunk (*fetch_key)(const unsigned char *chal,
88                                         const int csize))
89 {
90         struct _auth_chunk ho_chal, ho_key = {0};
91
92         if (!userid || !password || !nonce) {
93                 ho_key.err = "make_key: missing uid, pass or nonce";
94                 return ho_key;
95         }
96         ho_chal = make_challenge(userid, password, nonce);
97         if (ho_chal.err) {
98                 ho_key.err = ho_chal.err;
99                 return ho_key;
100         }
101         if (secret && secsize) {
102                 ho_key = new_key(ho_chal.data, sizeof(ho_chal.data),
103                                 secret, secsize);
104         } else if (fetch_key) {
105                 ho_key = (*fetch_key)(ho_chal.data, sizeof(ho_chal.data));
106         } else {
107                 ho_key.err = "make_key: neither secret nor fetch_key present";
108         }
109         memset(&ho_chal, 0, sizeof(ho_chal));
110         return ho_key;
111 }
112
113 static struct _auth_obj
114 make_authobj(const char *userid, const char *password, const char *nonce,
115                 const unsigned char *secret, const int secsize,
116                 const unsigned char *payload, const int paylsize)
117 {
118         struct _auth_obj ao = {0};
119         unsigned long rc;
120         unsigned char *data;
121         int datasize;
122         unsigned char datahash[HASHSIZE];
123         int datahashsize = HASHSIZE;
124         serializer_t srl;
125
126         datasize = ((secsize + paylsize + HASHSIZE + 4 * sizeof(short) - 1) /
127                         CBLKSIZE + 1) * CBLKSIZE;
128         data = alloca(datasize);
129         serial_init(&srl, data, datasize);
130         if (serial_put(&srl, secret, secsize) != secsize) {
131                 ao.err = "authobj: serialization of secret failed";
132         } else if (serial_put(&srl, payload, paylsize) != paylsize) {
133                 ao.err = "authobj: serialization of payload failed";
134         } else if ((rc = hash(data, serial_size(&srl),
135                                 datahash, &datahashsize))) {
136                 ao.err = crypto_errstr(rc);
137         } else if (serial_put(&srl, datahash, datahashsize) != datahashsize) {
138                 ao.err = "authobj: serialization of hash failed";
139         } else if (serial_put(&srl, NULL, 0) != 0) {
140                 ao.err = "authobj: serialization of terminator failed";
141         } else {
142                 unsigned long lrc;
143                 int osize = ((serial_size(&srl) -1) / CBLKSIZE + 1) * CBLKSIZE;
144                 struct _auth_chunk ho_key;
145
146                 ho_key = make_key(userid, password, nonce,
147                                         secret, secsize, NULL);
148                 if (ho_key.err) {
149                         ao.err = ho_key.err;
150                 } else if ((ao.buffer = malloc(osize + paylsize)) == NULL) {
151                         ao.err = "make authobj: malloc failed";
152                 } else if ((lrc = encrypt(ho_key.data, CBLKSIZE, data,
153                                         ao.buffer, osize))) {
154                         ao.err = crypto_errstr(lrc);
155                 } else {
156                         ao.data = ao.buffer;
157                         ao.datasize = osize;
158                         if (payload && paylsize) {
159                                 /* payload passthrough */
160                                 ao.payload = ao.data + osize;
161                                 memcpy(ao.payload, payload, paylsize);
162                                 ao.paylsize = paylsize;
163                         }
164                 }
165                 memset(&ho_key, 0, sizeof(ho_key));
166         }
167         memset(data, 0, datasize);
168         return ao;
169 }
170
171 static struct _auth_obj
172 parse_authobj(const char *userid, const char *password, const char *nonce,
173                 const unsigned char *secret, const int secsize,
174                 const unsigned char *ablob, const int blobsize,
175                 struct _auth_chunk (*fetch_key)(const unsigned char *chal,
176                                                 const int csize))
177 {
178         unsigned long rc;
179         struct _auth_obj ao = {0};
180         struct _auth_chunk ho_key;
181
182         ho_key = make_key(userid, password, nonce, secret, secsize, fetch_key);
183         if (ho_key.err) {
184                 ao.err = ho_key.err;
185         } else if ((ao.buffer = malloc(blobsize)) == NULL) {
186                 ao.err = "parse authobj: malloc failed";
187         } else if ((rc = decrypt(ho_key.data, CBLKSIZE,
188                                 ablob, ao.buffer, blobsize))) {
189                 ao.err = crypto_errstr(rc);
190         } else {
191                 serializer_t srl;
192                 unsigned char myhash[HASHSIZE];
193                 int myhsize = HASHSIZE;
194                 unsigned char *theirhash;
195                 int theirhsize;
196                 unsigned long rc;
197
198                 serial_init(&srl, ao.buffer, blobsize);
199                 if (serial_get(&srl, (void**)&ao.data, &ao.datasize)) {
200                         ao.err = "mismatch: impossible secret";
201                 } else if (serial_get(&srl, (void**)&ao.payload, &ao.paylsize)) {
202                         ao.err = "mismatch: impossible payload";
203                 } else if ((rc = hash(ao.buffer, serial_size(&srl),
204                                         myhash, &myhsize))) {
205                         ao.err = crypto_errstr(rc);
206                 } else if (serial_get(&srl, (void**)&theirhash, &theirhsize)) {
207                         ao.err = "mismatch: impossible hash";
208                 } else if (theirhsize != HASHSIZE) {
209                         ao.err = "mismatch: hash is of wrong size";
210                 } else if ((myhsize != theirhsize) ||
211                                 memcmp(myhash, theirhash, myhsize)) {
212                         ao.err = "mismatch: different hash";
213                 }
214         }
215         memset(&ho_key, 0, sizeof(ho_key));
216         return ao;
217 }
218
219 struct _auth_obj authobj(const char *userid, const char *password,
220                 const char *oldnonce, const char *newnonce,
221                 const unsigned char *secret, const int secsize,
222                 const unsigned char *payload, const int paylsize,
223                 const unsigned char *ablob, const int blobsize,
224                 struct _auth_chunk (*fetch_key)(const unsigned char *chal,
225                                                 const int csize))
226 {
227         const unsigned char *wsecret;
228         int wsecsize;
229         const unsigned char *wpayload;
230         int wpaylsize;
231         struct _auth_obj old_ao = {0};
232         struct _auth_obj new_ao = {0};
233
234         if (!secret || !secsize || !payload) {
235                 old_ao = parse_authobj(userid, password, oldnonce,
236                                         secret, secsize,
237                                         ablob, blobsize, fetch_key);
238                 if (old_ao.err) {
239                         new_ao.err = old_ao.err;
240                         if (old_ao.buffer) free(old_ao.buffer);
241                         return new_ao;
242                 } else {
243                         if (secret && secsize) {
244                                 wsecret = secret;
245                                 wsecsize = secsize;
246                         } else {
247                                 wsecret = old_ao.data;
248                                 wsecsize = old_ao.datasize;
249                         }
250                         if (payload) {
251                                 wpayload = payload;
252                                 wpaylsize = paylsize;
253                         } else {
254                                 wpayload = old_ao.payload;
255                                 wpaylsize = old_ao.paylsize;
256                         }
257                 }
258         } else {
259                 wsecret = secret;
260                 wsecsize = secsize;
261                 wpayload = payload;
262                 wpaylsize = paylsize;
263         }
264
265
266         new_ao = make_authobj(userid, password, newnonce,
267                                 wsecret, wsecsize, wpayload, wpaylsize);
268
269         if (old_ao.data) memset(old_ao.data, 0, old_ao.datasize);
270         if (old_ao.payload) memset(old_ao.payload, 0, old_ao.paylsize);
271         if (old_ao.buffer) free(old_ao.buffer);
272         return new_ao;
273 }