authobj - new interface works
[pam_pcsc_cr.git] / authobj.c
1 #ifdef HAVE_CONFIG_H
2 # include "config.h"
3 #endif
4 #include <stdio.h>
5 #include <string.h>
6 #include <alloca.h>
7 #include "serial.h"
8 #include "crypto.h"
9 #include "authobj.h"
10 #include "pcsc_cr.h"
11
12 struct _hash_obj {
13         const char *err;
14         unsigned char hash[HASHSIZE];
15 };
16
17 static struct _hash_obj
18 make_challenge(const char *uid, const char *pass, const char *nonce)
19 {
20         struct _hash_obj ho = {0};
21         unsigned long rc;
22         serializer_t srl;
23         int datasize = strlen(uid) + strlen(pass) + strlen(nonce) +
24                         4 * sizeof(short);
25         unsigned char *data = alloca(datasize);
26         int hashsize = sizeof(ho.hash);
27
28         serial_init(&srl, data, datasize);
29         if (serial_put(&srl, uid, strlen(uid)) != strlen(uid)) {
30                 ho.err = "challenge: serialization of uid failed";
31         } else if (serial_put(&srl, pass, strlen(pass)) != strlen(pass)) {
32                 ho.err = "challenge: serialization of pass failed";
33         } else if (serial_put(&srl, nonce, strlen(nonce)) != strlen(nonce)) {
34                 ho.err = "challenge: serialization of nonce failed";
35         } else if (serial_put(&srl, NULL, 0) != 0) {
36                 ho.err = "challenge: serialization of terminator failed";
37         }
38         if (!ho.err) {
39                 if ((rc = hash(data, serial_size(&srl), &ho.hash, &hashsize))) {
40                         ho.err = crypto_errstr(rc);
41                 } else if (hashsize != sizeof(ho.hash)) {
42                         ho.err = "challenge: hash size is wrong";
43                 }
44         }
45         memset(data, 0, datasize);
46         return ho;
47 }
48
49 static struct _hash_obj
50 make_key(const unsigned char *challenge, const int challengesize,
51         const unsigned char *secret, const int secsize)
52 {
53         struct _hash_obj ho = {0};
54         unsigned long rc;
55         int keysize = sizeof(ho.hash);
56
57         if ((rc = hmac(secret, secsize, challenge, challengesize,
58                         &ho.hash, &keysize))) {
59                 ho.err = crypto_errstr(rc);
60         } else if (keysize != sizeof(ho.hash)) {
61                 ho.err = "make_key: hash size is wrong";
62         }
63         return ho;
64 }
65
66 static struct _hash_obj
67 fetch_key(const unsigned char *challenge, const int challengesize)
68 {
69         struct _hash_obj ho = {0};
70         long rc;
71         int keysize = sizeof(ho.hash);
72
73         if ((rc = pcsc_cr(challenge, challengesize, ho.hash, &keysize))) {
74                 ho.err = pcsc_errstr(rc);
75         }
76         return ho;
77 }
78
79 static struct _auth_obj
80 make_authobj(const unsigned char *key, const int keysize,
81                 const unsigned char *secret, const int secsize,
82                 const unsigned char *payload, const int paysize)
83 {
84         struct _auth_obj ao = {0};
85         unsigned long rc;
86         unsigned char *data;
87         int datasize;
88         unsigned char datahash[HASHSIZE];
89         int datahashsize = HASHSIZE;
90         serializer_t srl;
91
92         if (keysize < CBLKSIZE) {
93                 ao.err = "make authobj: key too short";
94                 return ao;
95         }
96         datasize = ((secsize + paysize + HASHSIZE + 4 * sizeof(short) - 1) /
97                         CBLKSIZE + 1) * CBLKSIZE;
98         data = alloca(datasize);
99         serial_init(&srl, data, datasize);
100         if (serial_put(&srl, secret, secsize) != secsize) {
101                 ao.err = "authobj: serialization of secret failed";
102         } else if (serial_put(&srl, payload, paysize) != paysize) {
103                 ao.err = "authobj: serialization of payload failed";
104         } else if ((rc = hash(data, serial_size(&srl),
105                                 datahash, &datahashsize))) {
106                 ao.err = crypto_errstr(rc);
107         } else if (serial_put(&srl, datahash, datahashsize) != datahashsize) {
108                 ao.err = "authobj: serialization of hash failed";
109         } else if (serial_put(&srl, NULL, 0) != 0) {
110                 ao.err = "authobj: serialization of terminator failed";
111         }
112
113         if (!ao.err) {
114                 unsigned long lrc;
115                 int osize = ((serial_size(&srl) -1) / CBLKSIZE + 1) * CBLKSIZE;
116
117                 if ((ao.buffer = malloc(osize + paysize)) == NULL) {
118                         ao.err = "make authobj: malloc failed";
119                 } else if ((lrc = encrypt(key, CBLKSIZE, data,
120                                         ao.buffer, osize))) {
121                         ao.err = crypto_errstr(lrc);
122                 } else {
123                         ao.data = ao.buffer;
124                         ao.datasize = osize;
125                         if (payload && paysize) {
126                                 /* payload passthrough */
127                                 ao.payload = ao.data + osize;
128                                 memcpy(ao.payload, payload, paysize);
129                                 ao.paylsize = paysize;
130                         }
131                 }
132         }
133
134         memset(data, 0, datasize);
135         return ao;
136 }
137
138 static struct _auth_obj
139 parse_authobj(const unsigned char *key, const int keysize,
140                 const unsigned char *buffer, const int bufsize)
141 {
142         unsigned long rc;
143         struct _auth_obj ao = {0};
144
145         if (keysize < CBLKSIZE) {
146                 ao.err = "parse authobj: key too short";
147         } else if ((ao.buffer = malloc(bufsize)) == NULL) {
148                 ao.err = "parse authobj: malloc failed";
149         } else if ((rc = decrypt(key, CBLKSIZE, buffer, ao.buffer, bufsize))) {
150                 ao.err = crypto_errstr(rc);
151         } else {
152                 serializer_t srl;
153                 unsigned char myhash[HASHSIZE];
154                 int myhsize = HASHSIZE;
155                 unsigned char *theirhash;
156                 int theirhsize;
157                 unsigned long rc;
158
159                 serial_init(&srl, ao.buffer, bufsize);
160                 if (serial_get(&srl, (void**)&ao.data, &ao.datasize)) {
161                         ao.err = "mismatch: impossible secret";
162                 } else if (serial_get(&srl, (void**)&ao.payload, &ao.paylsize)) {
163                         ao.err = "mismatch: impossible payload";
164                 } else if ((rc = hash(ao.buffer, serial_size(&srl),
165                                         myhash, &myhsize))) {
166                         ao.err = crypto_errstr(rc);
167                 } else if (serial_get(&srl, (void**)&theirhash, &theirhsize)) {
168                         ao.err = "mismatch: impossible hash";
169                 } else if (theirhsize != HASHSIZE) {
170                         ao.err = "mismatch: hash is of wrong size";
171                 } else if ((myhsize != theirhsize) ||
172                                 memcmp(myhash, theirhash, myhsize)) {
173                         ao.err = "mismatch: different hash";
174                 }
175         }
176         return ao;
177 }
178
179 struct _auth_obj new_authobj(const char *userid, const char *password,
180                                 const char *nonce,
181                         const unsigned char *secret, const int secsize,
182                         const unsigned char *payload, const int paysize)
183 {
184         struct _auth_obj new_ao = {0};
185         struct _hash_obj ho_chal, ho_key;
186
187         ho_chal = make_challenge(userid, password, nonce);
188         if (ho_chal.err) {
189                 new_ao.err = ho_chal.err;
190                 return new_ao;
191         }
192         ho_key = make_key(ho_chal.hash, sizeof(ho_chal.hash), secret, secsize);
193         memset(&ho_chal, 0, sizeof(ho_chal));
194         if (ho_key.err) {
195                 new_ao.err = ho_key.err;
196                 return new_ao;
197         }
198         new_ao = make_authobj(ho_key.hash, sizeof(ho_key.hash),
199                         secret, secsize, payload, paysize);
200         memset(&ho_key, 0, sizeof(ho_key));
201         return new_ao;
202 }
203
204 struct _auth_obj verify_authobj(const char *userid, const char *password,
205                                 const char *oldnonce, const char *newnonce,
206                         const unsigned char *authobj, const int authsize)
207 {
208         struct _auth_obj old_ao;
209         struct _auth_obj new_ao = {0};
210         struct _hash_obj ho_chal, ho_key;
211
212         ho_chal = make_challenge(userid, password, oldnonce);
213         if (ho_chal.err) {
214                 new_ao.err = ho_chal.err;
215                 return new_ao;
216         }
217         ho_key = fetch_key(ho_chal.hash, sizeof(ho_chal.hash));
218         memset(&ho_chal, 0, sizeof(ho_chal));
219         if (ho_key.err) {
220                 new_ao.err = ho_key.err;
221                 return new_ao;
222         }
223         old_ao = parse_authobj(ho_key.hash, sizeof(ho_key.hash),
224                                 authobj, authsize);
225         memset(&ho_key, 0, sizeof(ho_key));
226         if (old_ao.err) {
227                 new_ao.err = old_ao.err;
228                 if (old_ao.buffer) free(old_ao.buffer);
229                 return new_ao;
230         }
231
232         ho_chal = make_challenge(userid, password, newnonce);
233         if (ho_chal.err) {
234                 new_ao.err = ho_chal.err;
235                 return new_ao;
236         }
237         ho_key = make_key(ho_chal.hash, sizeof(ho_chal.hash),
238                                 old_ao.data, old_ao.datasize);
239         memset(&ho_chal, 0, sizeof(ho_chal));
240         if (ho_key.err) {
241                 new_ao.err = ho_key.err;
242                 return new_ao;
243         }
244         new_ao = make_authobj(ho_key.hash, sizeof(ho_key.hash),
245                         old_ao.data, old_ao.datasize,
246                         old_ao.payload, old_ao.paylsize);
247         memset(&ho_key, 0, sizeof(ho_key));
248
249         if (old_ao.data) memset(old_ao.data, 0, old_ao.datasize);
250         if (old_ao.payload) memset(old_ao.payload, 0, old_ao.paylsize);
251         if (old_ao.buffer) free(old_ao.buffer);
252         return new_ao;
253 }
254
255 struct _auth_obj reload_authobj(const char *userid, const char *password,
256                                 const char *oldnonce, const char *newnonce,
257                         const unsigned char *authobj, const int authsize,
258                         const unsigned char *payload, const int paysize)
259 {
260         struct _auth_obj old_ao;
261         struct _auth_obj new_ao = {0};
262         struct _hash_obj ho_chal, ho_key;
263
264         ho_chal = make_challenge(userid, password, oldnonce);
265         if (ho_chal.err) {
266                 new_ao.err = ho_chal.err;
267                 return new_ao;
268         }
269         ho_key = fetch_key(ho_chal.hash, sizeof(ho_chal.hash));
270         memset(&ho_chal, 0, sizeof(ho_chal));
271         if (ho_key.err) {
272                 new_ao.err = ho_key.err;
273                 return new_ao;
274         }
275         memset(&ho_key, 0, sizeof(ho_key));
276         return new_ao;
277 }