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