change serialization interface
[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         int 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         datasize = ((secsize + paysize + HASHSIZE + 4 * sizeof(short) - 1) /
93                         CBLKSIZE + 1) * CBLKSIZE;
94         data = alloca(datasize);
95         serial_init(&srl, data, datasize);
96         if (serial_put(&srl, secret, secsize) != secsize) {
97                 ao.err = "authobj: serialization of secret failed";
98         } else if (serial_put(&srl, payload, paysize) != paysize) {
99                 ao.err = "authobj: serialization of payload failed";
100         } else if ((rc = hash(data, serial_size(&srl),
101                                 datahash, &datahashsize))) {
102                 ao.err = crypto_errstr(rc);
103         } else if (serial_put(&srl, datahash, datahashsize) != datahashsize) {
104                 ao.err = "authobj: serialization of hash failed";
105         } else if (serial_put(&srl, NULL, 0) != 0) {
106                 ao.err = "authobj: serialization of terminator failed";
107         }
108
109         if (!ao.err) {
110                 unsigned long lrc;
111                 int osize = ((serial_size(&srl) -1) / CBLKSIZE + 1) * CBLKSIZE;
112
113                 if ((ao.buffer = malloc(osize)) == NULL) {
114                         ao.err = "authobj: malloc failed";
115                 } else if ((lrc = encrypt(key, CBLKSIZE, data,
116                                         ao.buffer, osize))) {
117                         ao.err = crypto_errstr(lrc);
118                 } else {
119                         ao.authobj = ao.buffer;
120                         ao.authsize = osize;
121                 }
122         }
123
124         memset(data, 0, datasize);
125         return ao;
126 }
127
128 static int parse_authobj(const unsigned char *key, const int keysize,
129                 const unsigned char *buffer, const int bufsize,
130                 unsigned char *secret, int *secsize,
131                 unsigned char *payload, int *paysize)
132 {
133         int datasize = bufsize;
134         unsigned char *data = alloca(datasize);
135         serializer_t srl;
136         int tsize;
137         unsigned char myhash[HASHSIZE];
138         int myhashsize = HASHSIZE;
139         unsigned char theirhash[HASHSIZE];
140         int theirhashsize = HASHSIZE;
141
142         if (decrypt(key, CBLKSIZE, buffer, data, datasize))
143                 return 1;
144         serial_init(&srl, data, datasize);
145         tsize = *secsize;
146         *secsize = serial_get(&srl, secret, tsize);
147         if (*secsize > tsize || *secsize <= 0) return 1;
148         tsize = *paysize;
149         *paysize = serial_get(&srl, payload, tsize);
150         if (*paysize > tsize || *paysize <= 0) return 1;
151         if (hash(data, serial_size(&srl), myhash, &myhashsize))
152                 return 1;
153         theirhashsize = serial_get(&srl, theirhash, theirhashsize);
154         if (theirhashsize != HASHSIZE) return 1;
155         if ((myhashsize != theirhashsize) ||
156             memcmp(myhash, theirhash, myhashsize))
157                 return 1;
158         return 0;
159 }
160
161 struct _auth_obj new_authobj(const char *userid, const char *password,
162                                 const char *nonce,
163                         const unsigned char *secret, const int secsize,
164                         const unsigned char *payload, const int paysize)
165 {
166         struct _auth_obj ao = {0};
167         struct _hash_obj ho_chal, ho_key;
168
169         ho_chal = make_challenge(userid, password, nonce);
170         if (ho_chal.err) {
171                 ao.err = ho_chal.err;
172                 return ao;
173         }
174         ho_key = make_key(ho_chal.hash, sizeof(ho_chal.hash), secret, secsize);
175         if (ho_key.err) {
176                 ao.err = ho_key.err;
177                 return ao;
178         }
179         ao = make_authobj(ho_key.hash, sizeof(ho_key.hash),
180                         secret, secsize, payload, paysize);
181         memset(&ho_chal, 0, sizeof(ho_chal));
182         memset(&ho_key, 0, sizeof(ho_key));
183         return ao;
184 }
185
186 struct _auth_obj verify_authobj(const char *userid, const char *password,
187                                 const char *oldnonce, const char *newnonce,
188                         const unsigned char *authobj, const int authsize)
189 {
190         struct _auth_obj ao = {0};
191         struct _hash_obj ho_chal, ho_key;
192
193         ho_chal = make_challenge(userid, password, oldnonce);
194         if (ho_chal.err) {
195                 ao.err = ho_chal.err;
196                 return ao;
197         }
198         ho_key = fetch_key(ho_chal.hash, sizeof(ho_chal.hash));
199         if (ho_key.err) {
200                 ao.err = ho_key.err;
201                 return ao;
202         }
203         memset(&ho_chal, 0, sizeof(ho_chal));
204         memset(&ho_key, 0, sizeof(ho_key));
205         return ao;
206 }
207
208 struct _auth_obj reload_authobj(const char *userid, const char *password,
209                                 const char *oldnonce, const char *newnonce,
210                         const unsigned char *authobj, const int authsize,
211                         const unsigned char *payload, const int paysize)
212 {
213         struct _auth_obj ao = {0};
214         struct _hash_obj ho_chal, ho_key;
215
216         ho_chal = make_challenge(userid, password, oldnonce);
217         if (ho_chal.err) {
218                 ao.err = ho_chal.err;
219                 return ao;
220         }
221         ho_key = fetch_key(ho_chal.hash, sizeof(ho_chal.hash));
222         if (ho_key.err) {
223                 ao.err = ho_key.err;
224                 return ao;
225         }
226         memset(&ho_chal, 0, sizeof(ho_chal));
227         memset(&ho_key, 0, sizeof(ho_key));
228         return ao;
229 }