Imported Upstream version 0.9.0
[deb_shairplay.git] / src / lib / digest.c
1 #include <stdlib.h>
2 #include <stdio.h>
3 #include <stdint.h>
4 #include <string.h>
5
6 #include "compat.h"
7 #include "utils.h"
8 #include "crypto/crypto.h"
9
10 void
11 digest_md5_to_hex(const unsigned char *md5buf, char *md5hex)
12 {
13 int i;
14 for (i=0; i<MD5_SIZE*2; i++) {
15 int val = (i%2) ? md5buf[i/2]&0x0f : (md5buf[i/2]&0xf0)>>4;
16 md5hex[i] = (val<10) ? '0'+val : 'a'+(val-10);
17 }
18 }
19
20 void
21 digest_get_response(const char *username, const char *realm,
22 const char *password, const char *nonce,
23 const char *method, const char *uri,
24 char *response)
25 {
26 MD5_CTX md5ctx;
27 unsigned char md5buf[MD5_SIZE];
28 char md5hex[MD5_SIZE*2];
29
30 /* Calculate first inner MD5 hash */
31 MD5_Init(&md5ctx);
32 MD5_Update(&md5ctx, (const unsigned char *)username, strlen(username));
33 MD5_Update(&md5ctx, (const unsigned char *)":", 1);
34 MD5_Update(&md5ctx, (const unsigned char *)realm, strlen(realm));
35 MD5_Update(&md5ctx, (const unsigned char *)":", 1);
36 MD5_Update(&md5ctx, (const unsigned char *)password, strlen(password));
37 MD5_Final(md5buf, &md5ctx);
38 digest_md5_to_hex(md5buf, md5hex);
39
40 /* Calculate second inner MD5 hash */
41 MD5_Init(&md5ctx);
42 MD5_Update(&md5ctx, (const unsigned char *)method, strlen(method));
43 MD5_Update(&md5ctx, (const unsigned char *)":", 1);
44 MD5_Update(&md5ctx, (const unsigned char *)uri, strlen(uri));
45 MD5_Final(md5buf, &md5ctx);
46
47 /* Calculate outer MD5 hash */
48 MD5_Init(&md5ctx);
49 MD5_Update(&md5ctx, (const unsigned char *)md5hex, sizeof(md5hex));
50 MD5_Update(&md5ctx, (const unsigned char *)":", 1);
51 MD5_Update(&md5ctx, (const unsigned char *)nonce, strlen(nonce));
52 MD5_Update(&md5ctx, (const unsigned char *)":", 1);
53 digest_md5_to_hex(md5buf, md5hex);
54 MD5_Update(&md5ctx, (const unsigned char *)md5hex, sizeof(md5hex));
55 MD5_Final(md5buf, &md5ctx);
56
57 /* Store the final result to response */
58 digest_md5_to_hex(md5buf, response);
59 }
60
61 void
62 digest_generate_nonce(char *result, int resultlen)
63 {
64 MD5_CTX md5ctx;
65 unsigned char md5buf[MD5_SIZE];
66 char md5hex[MD5_SIZE*2];
67 unsigned int time;
68
69 SYSTEM_GET_TIME(time);
70
71 MD5_Init(&md5ctx);
72 MD5_Update(&md5ctx, (unsigned char *)&time, sizeof(time));
73 MD5_Final(md5buf, &md5ctx);
74 digest_md5_to_hex(md5buf, md5hex);
75
76 memset(result, 0, resultlen);
77 strncpy(result, md5hex, resultlen-1);
78 }
79
80 int
81 digest_is_valid(const char *our_realm, const char *password,
82 const char *our_nonce, const char *method,
83 const char *our_uri, const char *authorization)
84 {
85 char *auth;
86 char *current;
87 char *value;
88 int success;
89
90 /* Get values from authorization */
91 char *username = NULL;
92 char *realm = NULL;
93 char *nonce = NULL;
94 char *uri = NULL;
95 char *response = NULL;
96
97 /* Buffer for our response */
98 char our_response[MD5_SIZE*2+1];
99
100 if (!authorization) {
101 return 0;
102 }
103 current = auth = strdup(authorization);
104 if (!auth) {
105 return 0;
106 }
107
108 /* Check that the type is digest */
109 if (strncmp("Digest", current, 6)) {
110 free(auth);
111 return 0;
112 }
113 current += 6;
114
115 while ((value = utils_strsep(&current, ",")) != NULL) {
116 char *first, *last;
117
118 /* Find first and last characters */
119 first = value;
120 last = value+strlen(value)-1;
121
122 /* Trim spaces from the value */
123 while (*first == ' ' && first < last) first++;
124 while (*last == ' ' && last > first) last--;
125
126 /* Validate the last character */
127 if (*last != '"') continue;
128 else *last = '\0';
129
130 /* Store value if it is relevant */
131 if (!strncmp("username=\"", first, 10))
132 username = first+10;
133 if (!strncmp("realm=\"", first, 7))
134 realm = first+7;
135 if (!strncmp("nonce=\"", first, 7))
136 nonce = first+7;
137 if (!strncmp("uri=\"", first, 5))
138 uri = first+5;
139 if (!strncmp("response=\"", first, 10))
140 response = first+10;
141 }
142
143 if (!username || !realm || !nonce || !uri || !response) {
144 return 0;
145 }
146 if (strcmp(realm, our_realm) || strcmp(nonce, our_nonce) || strcmp(uri, our_uri)) {
147 return 0;
148 }
149
150 /* Calculate our response */
151 memset(our_response, 0, sizeof(our_response));
152 digest_get_response(username, realm, password, nonce,
153 method, uri, our_response);
154 success = !strcmp(response, our_response);
155 free(auth);
156
157 return success;
158 }
159
160