1/***
2  This file is part of PulseAudio.
3
4  Copyright 2008 Colin Guthrie
5  Copyright Kungliga Tekniska högskolan
6  Copyright 2013 Martin Blanchard
7
8  PulseAudio is free software; you can redistribute it and/or modify
9  it under the terms of the GNU Lesser General Public License as published
10  by the Free Software Foundation; either version 2.1 of the License,
11  or (at your option) any later version.
12
13  PulseAudio is distributed in the hope that it will be useful, but
14  WITHOUT ANY WARRANTY; without even the implied warranty of
15  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16  General Public License for more details.
17
18  You should have received a copy of the GNU Lesser General Public License
19  along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
20***/
21
22/***
23  The base64 implementation was originally inspired by a file developed
24  by Kungliga Tekniska högskolan.
25***/
26
27#ifdef HAVE_CONFIG_H
28#include <config.h>
29#endif
30
31#include <stdlib.h>
32#include <string.h>
33
34#include <openssl/err.h>
35#include <openssl/md5.h>
36
37#include <pulse/xmalloc.h>
38
39#include <pulsecore/core-util.h>
40#include <pulsecore/macro.h>
41
42#include "raop-util.h"
43
44#ifndef MD5_DIGEST_LENGTH
45#define MD5_DIGEST_LENGTH 16
46#endif
47
48#define MD5_HASH_LENGTH (2*MD5_DIGEST_LENGTH)
49
50#define BASE64_DECODE_ERROR 0xffffffff
51
52static const char base64_chars[] =
53    "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
54
55static int char_position(char c) {
56    if (c >= 'A' && c <= 'Z')
57        return c - 'A' + 0;
58    if (c >= 'a' && c <= 'z')
59        return c - 'a' + 26;
60    if (c >= '0' && c <= '9')
61        return c - '0' + 52;
62    if (c == '+')
63        return 62;
64    if (c == '/')
65        return 63;
66
67    return -1;
68}
69
70static unsigned int token_decode(const char *token) {
71    unsigned int val = 0;
72    int marker = 0;
73    int i;
74
75    if (strlen(token) < 4)
76        return BASE64_DECODE_ERROR;
77    for (i = 0; i < 4; i++) {
78        val *= 64;
79        if (token[i] == '=')
80            marker++;
81        else if (marker > 0)
82            return BASE64_DECODE_ERROR;
83        else {
84            int lpos = char_position(token[i]);
85            if (lpos < 0)
86                return BASE64_DECODE_ERROR;
87            val += lpos;
88        }
89    }
90
91    if (marker > 2)
92        return BASE64_DECODE_ERROR;
93
94    return (marker << 24) | val;
95}
96
97int pa_raop_base64_encode(const void *data, int len, char **str) {
98    const unsigned char *q;
99    char *p, *s = NULL;
100    int i, c;
101
102    pa_assert(data);
103    pa_assert(str);
104
105    p = s = pa_xnew(char, len * 4 / 3 + 4);
106    q = (const unsigned char *) data;
107    for (i = 0; i < len;) {
108        c = q[i++];
109        c *= 256;
110        if (i < len)
111            c += q[i];
112        i++;
113        c *= 256;
114        if (i < len)
115            c += q[i];
116        i++;
117        p[0] = base64_chars[(c & 0x00fc0000) >> 18];
118        p[1] = base64_chars[(c & 0x0003f000) >> 12];
119        p[2] = base64_chars[(c & 0x00000fc0) >> 6];
120        p[3] = base64_chars[(c & 0x0000003f) >> 0];
121        if (i > len)
122            p[3] = '=';
123        if (i > len + 1)
124            p[2] = '=';
125        p += 4;
126    }
127
128    *p = 0;
129    *str = s;
130    return strlen(s);
131}
132
133int pa_raop_base64_decode(const char *str, void *data) {
134    const char *p;
135    unsigned char *q;
136
137    pa_assert(str);
138    pa_assert(data);
139
140    q = data;
141    for (p = str; *p && (*p == '=' || strchr(base64_chars, *p)); p += 4) {
142        unsigned int val = token_decode(p);
143        unsigned int marker = (val >> 24) & 0xff;
144        if (val == BASE64_DECODE_ERROR)
145            return -1;
146        *q++ = (val >> 16) & 0xff;
147        if (marker < 2)
148            *q++ = (val >> 8) & 0xff;
149        if (marker < 1)
150            *q++ = val & 0xff;
151    }
152
153    return q - (unsigned char *) data;
154}
155
156int pa_raop_md5_hash(const char *data, int len, char **str) {
157    unsigned char d[MD5_DIGEST_LENGTH];
158    char *s = NULL;
159    int i;
160
161    pa_assert(data);
162    pa_assert(str);
163
164    MD5((unsigned char*) data, len, d);
165    s = pa_xnew(char, MD5_HASH_LENGTH);
166    for (i = 0; i < MD5_DIGEST_LENGTH; i++)
167        sprintf(&s[2*i], "%02x", (unsigned int) d[i]);
168
169    *str = s;
170    s[MD5_HASH_LENGTH] = 0;
171    return strlen(s);
172}
173
174int pa_raop_basic_response(const char *user, const char *pwd, char **str) {
175    char *tmp, *B = NULL;
176
177    pa_assert(str);
178
179    tmp = pa_sprintf_malloc("%s:%s", user, pwd);
180    pa_raop_base64_encode(tmp, strlen(tmp), &B);
181    pa_xfree(tmp);
182
183    *str = B;
184    return strlen(B);
185}
186
187int pa_raop_digest_response(const char *user, const char *realm, const char *password,
188                            const char *nonce, const char *uri, char **str) {
189    char *A1, *HA1, *A2, *HA2;
190    char *tmp, *KD = NULL;
191
192    pa_assert(str);
193
194    A1 = pa_sprintf_malloc("%s:%s:%s", user, realm, password);
195    pa_raop_md5_hash(A1, strlen(A1), &HA1);
196    pa_xfree(A1);
197
198    A2 = pa_sprintf_malloc("OPTIONS:%s", uri);
199    pa_raop_md5_hash(A2, strlen(A2), &HA2);
200    pa_xfree(A2);
201
202    tmp = pa_sprintf_malloc("%s:%s:%s", HA1, nonce, HA2);
203    pa_raop_md5_hash(tmp, strlen(tmp), &KD);
204    pa_xfree(tmp);
205
206    pa_xfree(HA1);
207    pa_xfree(HA2);
208
209    *str = KD;
210    return strlen(KD);
211}
212