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);
130 We allocate memory rounded up to CBLKSIZE on the stack, but do not
131 use the last bytes. Stack protectors, if enabled, fill this memory
132 with `canary` value. Later, when encryption function is called,
133 stack protector detects that it tries to access "uninitialized
134 memory". Which, while technically true, is not an error. Still,
135 let us make stack protector happy by initializing the whole area:
137 memset(data, 0, datasize);
138 serial_init(&srl, data, datasize);
139 if (serial_put(&srl, secret, secsize) != secsize) {
140 ao.err = "authobj: serialization of secret failed";
141 } else if (serial_put(&srl, payload, paylsize) != paylsize) {
142 ao.err = "authobj: serialization of payload failed";
143 } else if ((rc = hash(data, serial_size(&srl),
144 datahash, &datahashsize))) {
145 ao.err = crypto_errstr(rc);
146 } else if (serial_put(&srl, datahash, datahashsize) != datahashsize) {
147 ao.err = "authobj: serialization of hash failed";
148 } else if (serial_put(&srl, NULL, 0) != 0) {
149 ao.err = "authobj: serialization of terminator failed";
152 int osize = ((serial_size(&srl) -1) / CBLKSIZE + 1) * CBLKSIZE;
153 struct _auth_chunk ho_key;
155 ho_key = make_key(userid, password, nonce,
156 secret, secsize, NULL);
159 } else if ((ao.buffer = malloc(osize + paylsize)) == NULL) {
160 ao.err = "make authobj: malloc failed";
161 } else if ((lrc = encrypt(ho_key.data, CBLKSIZE, data,
162 ao.buffer, osize))) {
163 ao.err = crypto_errstr(lrc);
167 if (payload && paylsize) {
168 /* payload passthrough */
169 ao.payload = ao.data + osize;
170 memcpy(ao.payload, payload, paylsize);
171 ao.paylsize = paylsize;
174 memset(&ho_key, 0, sizeof(ho_key));
176 memset(data, 0, datasize);
180 static struct _auth_obj
181 parse_authobj(const char *userid, const char *password, const char *nonce,
182 const unsigned char *secret, const int secsize,
183 const unsigned char *ablob, const int blobsize,
184 struct _auth_chunk (*fetch_key)(const unsigned char *chal,
188 struct _auth_obj ao = {0};
189 struct _auth_chunk ho_key;
191 ho_key = make_key(userid, password, nonce, secret, secsize, fetch_key);
194 } else if ((ao.buffer = malloc(blobsize)) == NULL) {
195 ao.err = "parse authobj: malloc failed";
196 } else if ((rc = decrypt(ho_key.data, CBLKSIZE,
197 ablob, ao.buffer, blobsize))) {
198 ao.err = crypto_errstr(rc);
201 unsigned char myhash[HASHSIZE];
202 int myhsize = HASHSIZE;
203 unsigned char *theirhash;
207 serial_init(&srl, ao.buffer, blobsize);
208 if (serial_get(&srl, (void**)&ao.data, &ao.datasize)) {
209 ao.err = "mismatch: impossible secret";
210 } else if (serial_get(&srl, (void**)&ao.payload, &ao.paylsize)) {
211 ao.err = "mismatch: impossible payload";
212 } else if ((rc = hash(ao.buffer, serial_size(&srl),
213 myhash, &myhsize))) {
214 ao.err = crypto_errstr(rc);
215 } else if (serial_get(&srl, (void**)&theirhash, &theirhsize)) {
216 ao.err = "mismatch: impossible hash";
217 } else if (theirhsize != HASHSIZE) {
218 ao.err = "mismatch: hash is of wrong size";
219 } else if ((myhsize != theirhsize) ||
220 memcmp(myhash, theirhash, myhsize)) {
221 ao.err = "mismatch: different hash";
224 memset(&ho_key, 0, sizeof(ho_key));
228 struct _auth_obj authobj(const char *userid, const char *password,
229 const char *oldnonce, const char *newnonce,
230 const unsigned char *secret, const int secsize,
231 const unsigned char *payload, const int paylsize,
232 const unsigned char *ablob, const int blobsize,
233 struct _auth_chunk (*fetch_key)(const unsigned char *chal,
236 const unsigned char *wsecret;
238 const unsigned char *wpayload;
240 struct _auth_obj old_ao = {0};
241 struct _auth_obj new_ao = {0};
243 if (!secret || !secsize || !payload) {
244 if (!ablob || !blobsize) {
245 new_ao.err = "authobj: previous data not supplied";
248 old_ao = parse_authobj(userid, password, oldnonce,
250 ablob, blobsize, fetch_key);
252 new_ao.err = old_ao.err;
253 if (old_ao.buffer) free(old_ao.buffer);
256 if (secret && secsize) {
260 wsecret = old_ao.data;
261 wsecsize = old_ao.datasize;
265 wpaylsize = paylsize;
267 wpayload = old_ao.payload;
268 wpaylsize = old_ao.paylsize;
275 wpaylsize = paylsize;
279 new_ao = make_authobj(userid, password, newnonce,
280 wsecret, wsecsize, wpayload, wpaylsize);
282 if (old_ao.data) memset(old_ao.data, 0, old_ao.datasize);
283 if (old_ao.payload) memset(old_ao.payload, 0, old_ao.paylsize);
284 if (old_ao.buffer) free(old_ao.buffer);