2 Copyright (c) 2013 Eugene Crosser
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.
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:
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.
17 2. Altered source versions must be plainly marked as such, and must
18 not be misrepresented as being the original software.
20 3. This notice may not be removed or altered from any source
35 static struct _auth_chunk
36 make_challenge(const char *uid, const char *pass, const char *nonce)
38 struct _auth_chunk ho = {0};
41 int datasize = strlen(uid) + strlen(pass) + strlen(nonce) +
43 unsigned char *data = alloca(datasize);
44 int hashsize = sizeof(ho.data);
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";
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";
63 memset(data, 0, datasize);
67 static struct _auth_chunk
68 new_key(const unsigned char *challenge, const int challengesize,
69 const unsigned char *secret, const int secsize)
71 struct _auth_chunk ho = {0};
73 int keysize = sizeof(ho.data);
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";
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,
90 struct _auth_chunk ho_chal, ho_key = {0};
92 if (!userid || !password || !nonce) {
93 ho_key.err = "make_key: missing uid, pass or nonce";
96 ho_chal = make_challenge(userid, password, nonce);
98 ho_key.err = ho_chal.err;
101 if (secret && secsize) {
102 ho_key = new_key(ho_chal.data, sizeof(ho_chal.data),
104 } else if (fetch_key) {
105 ho_key = (*fetch_key)(ho_chal.data, sizeof(ho_chal.data));
107 ho_key.err = "make_key: neither secret nor fetch_key present";
109 memset(&ho_chal, 0, sizeof(ho_chal));
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)
118 struct _auth_obj ao = {0};
122 unsigned char datahash[HASHSIZE];
123 int datahashsize = HASHSIZE;
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";
143 int osize = ((serial_size(&srl) -1) / CBLKSIZE + 1) * CBLKSIZE;
144 struct _auth_chunk ho_key;
146 ho_key = make_key(userid, password, nonce,
147 secret, secsize, NULL);
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);
158 if (payload && paylsize) {
159 /* payload passthrough */
160 ao.payload = ao.data + osize;
161 memcpy(ao.payload, payload, paylsize);
162 ao.paylsize = paylsize;
165 memset(&ho_key, 0, sizeof(ho_key));
167 memset(data, 0, datasize);
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,
179 struct _auth_obj ao = {0};
180 struct _auth_chunk ho_key;
182 ho_key = make_key(userid, password, nonce, secret, secsize, fetch_key);
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);
192 unsigned char myhash[HASHSIZE];
193 int myhsize = HASHSIZE;
194 unsigned char *theirhash;
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";
215 memset(&ho_key, 0, sizeof(ho_key));
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,
227 const unsigned char *wsecret;
229 const unsigned char *wpayload;
231 struct _auth_obj old_ao = {0};
232 struct _auth_obj new_ao = {0};
234 if (!secret || !secsize || !payload) {
235 old_ao = parse_authobj(userid, password, oldnonce,
237 ablob, blobsize, fetch_key);
239 new_ao.err = old_ao.err;
240 if (old_ao.buffer) free(old_ao.buffer);
243 if (secret && secsize) {
247 wsecret = old_ao.data;
248 wsecsize = old_ao.datasize;
252 wpaylsize = paylsize;
254 wpayload = old_ao.payload;
255 wpaylsize = old_ao.paylsize;
262 wpaylsize = paylsize;
266 new_ao = make_authobj(userid, password, newnonce,
267 wsecret, wsecsize, wpayload, wpaylsize);
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);