1 /* 2 * Copyright (c) 2021-2024 Apple Inc. All rights reserved. 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * https://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 #include "mDNSFeatures.h" 18 #if MDNSRESPONDER_SUPPORTS(APPLE, DNSSECv2) 19 20 //====================================================================================================================== 21 // MARK: - Headers 22 23 #include "dns_obj_log.h" 24 #include "base_encoding.h" 25 #include "dns_common.h" 26 #include <string.h> // For memcpy(). 27 28 #include "dns_assert_macros.h" 29 #include "mdns_strict.h" 30 31 //====================================================================================================================== 32 // MARK: - Constants 33 34 static const char b64_table[] = { 35 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 36 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 37 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 38 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', 39 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 40 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 41 'w', 'x', 'y', 'z', '0', '1', '2', '3', 42 '4', '5', '6', '7', '8', '9', '+', '/' 43 }; 44 45 static const char b32_hex_table[] = { 46 '0', '1', '2', '3', '4', '5', '6', '7', 47 '8', '9', 'A', 'B', 'C', 'D', 'E', 'F', 48 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 49 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V' 50 }; 51 52 #define MAX_LENGTH_B64_ENCODING_DATA (SIZE_MAX * 3 / 4) 53 #define MAX_LENGTH_B32_HEX_ENCODING_DATA (SIZE_MAX * 5 / 8) 54 55 //====================================================================================================================== 56 // MARK: - Local Prototypes 57 58 static void 59 base_64_encode(const uint8_t * NONNULL data, size_t data_len, char * NONNULL out_encoded_str); 60 61 static void 62 base_32_hex_encode(const uint8_t * NONNULL data, size_t data_len, bool no_padding, char * NONNULL out_encoded_str); 63 64 //====================================================================================================================== 65 // MARK: - Public Functions 66 67 char * 68 base_x_encode(const base_encoding_type_t type, const uint8_t * const data, const size_t data_len, 69 char * const out_encoded_str_buf) 70 { 71 const size_t encoded_str_len = base_x_get_encoded_string_length(type, data_len); 72 73 char *encoded_str; 74 if (out_encoded_str_buf == NULL) { 75 encoded_str = mdns_malloc(encoded_str_len + 1); 76 require_return_value(encoded_str != NULL, NULL); 77 } else { 78 encoded_str = out_encoded_str_buf; 79 } 80 81 encoded_str[encoded_str_len] = '\0'; 82 83 switch (type) { 84 case base_encoding_type_base64: 85 base_64_encode(data, data_len, encoded_str); 86 break; 87 case base_encoding_type_base32_hex_with_padding: 88 base_32_hex_encode(data, data_len, false, encoded_str); 89 break; 90 case base_encoding_type_base32_hex_without_padding: 91 base_32_hex_encode(data, data_len, true, encoded_str); 92 break; 93 MDNS_COVERED_SWITCH_DEFAULT: 94 break; 95 } 96 97 return encoded_str; 98 } 99 100 //====================================================================================================================== 101 102 size_t 103 base_x_get_encoded_string_length(const base_encoding_type_t type, const size_t data_len) 104 { 105 size_t encoded_str_len; 106 switch (type) { 107 case base_encoding_type_base64: 108 require_action(data_len < MAX_LENGTH_B64_ENCODING_DATA, exit, encoded_str_len = 0); 109 encoded_str_len = (data_len + 2) / 3 * 4; 110 break; 111 case base_encoding_type_base32_hex_with_padding: 112 require_action(data_len < MAX_LENGTH_B32_HEX_ENCODING_DATA, exit, encoded_str_len = 0); 113 encoded_str_len = (data_len + 4) / 5 * 8; 114 break; 115 case base_encoding_type_base32_hex_without_padding: 116 require_action(data_len < MAX_LENGTH_B32_HEX_ENCODING_DATA, exit, encoded_str_len = 0); 117 encoded_str_len = data_len / 5 * 8; 118 switch (data_len % 5) { 119 case 0: 120 // encoded_str_len += 0; 121 break; 122 case 1: 123 encoded_str_len += 2; 124 break; 125 case 2: 126 encoded_str_len += 4; 127 break; 128 case 3: 129 encoded_str_len += 5; 130 break; 131 case 4: 132 encoded_str_len += 7; 133 break; 134 MDNS_COVERED_SWITCH_DEFAULT: 135 encoded_str_len = 0; 136 break; 137 } 138 break; 139 MDNS_COVERED_SWITCH_DEFAULT: 140 encoded_str_len = 0; 141 break; 142 } 143 144 exit: 145 return encoded_str_len; 146 } 147 148 //====================================================================================================================== 149 // MARK: - Private Functions 150 151 static void 152 base_64_encode(const uint8_t * const data, const size_t data_len, char * const out_encoded_str) 153 { 154 const uint8_t * data_ptr = data; 155 const uint8_t * const data_ptr_limit = data_ptr + data_len; 156 char * encoded_str_ptr = out_encoded_str; 157 158 while (data_ptr < data_ptr_limit) { 159 char encoded_buf[4]; 160 uint32_t encoded_size = 0; 161 162 const size_t remain = (size_t)(data_ptr_limit - data_ptr); 163 uint32_t quantum = 0; 164 165 // Get 24 bits from 3 bytes. 166 switch (remain) { 167 default: 168 case 3: quantum |= (uint32_t)data_ptr[2]; // bits 16 - 23 169 case 2: quantum |= ((uint32_t)data_ptr[1]) << 8; // bits 8 - 15 170 case 1: quantum |= ((uint32_t)data_ptr[0]) << 16; // bits 0 - 7 171 } 172 173 // Advance the data pointer. 174 data_ptr += MIN(remain, 3); 175 176 // Convert 24 bits to 4 characters. 177 switch (remain) { 178 default: 179 case 3: 180 encoded_buf[3] = b64_table[quantum & 0x3F]; 181 encoded_size = 4; 182 case 2: 183 encoded_buf[2] = b64_table[(quantum >> 6) & 0x3F]; 184 if (encoded_size == 0) encoded_size = 3; 185 case 1: 186 encoded_buf[1] = b64_table[(quantum >> 12) & 0x3F]; 187 encoded_buf[0] = b64_table[(quantum >> 18) & 0x3F]; 188 if (encoded_size == 0) encoded_size = 2; 189 } 190 191 // Fill the padding with '='. 192 for (size_t i = encoded_size; i < sizeof(encoded_buf); i++) { 193 encoded_buf[i] = '='; 194 } 195 196 // Move the current encoded string chunk to the returned buffer. 197 memcpy(encoded_str_ptr, encoded_buf, sizeof(encoded_buf)); 198 encoded_str_ptr += sizeof(encoded_buf); 199 } 200 } 201 202 //====================================================================================================================== 203 204 static void 205 base_32_hex_encode(const uint8_t * const data, const size_t data_len, const bool no_padding, char * const out_encoded_str) 206 { 207 const uint8_t * data_ptr = data; 208 const uint8_t * const data_ptr_limit = data_ptr + data_len; 209 char * encoded_str_ptr = out_encoded_str; 210 211 while (data_ptr < data_ptr_limit) { 212 char encoded_buf[8]; 213 uint32_t encoded_size = 0; 214 215 size_t remain = (size_t)(data_ptr_limit - data_ptr); 216 uint64_t quantum = 0; 217 218 // Get 40 bits from 8 bytes. 219 switch (remain) { 220 default: 221 case 5: quantum |= (uint64_t)data_ptr[4]; // bits 32 - 39 222 case 4: quantum |= ((uint64_t)data_ptr[3]) << 8; // bits 24 - 32 223 case 3: quantum |= ((uint64_t)data_ptr[2]) << 16; // bits 16 - 23 224 case 2: quantum |= ((uint64_t)data_ptr[1]) << 24; // bits 8 - 15 225 case 1: quantum |= ((uint64_t)data_ptr[0]) << 32; // bits 0 - 7 226 } 227 228 // Advance the data pointer. 229 data_ptr += MIN(remain, 5); 230 231 // Convert 40 bits to 8 characters. 232 switch (remain) { 233 default: 234 case 5: 235 encoded_buf[7] = b32_hex_table[quantum & 0x1F]; 236 encoded_size = 8; 237 case 4: 238 encoded_buf[6] = b32_hex_table[(quantum >> 5) & 0x1F]; 239 encoded_buf[5] = b32_hex_table[(quantum >> 10) & 0x1F]; 240 if (encoded_size == 0) encoded_size = 7; 241 case 3: 242 encoded_buf[4] = b32_hex_table[(quantum >> 15) & 0x1F]; 243 if (encoded_size == 0) encoded_size = 5; 244 case 2: 245 encoded_buf[3] = b32_hex_table[(quantum >> 20) & 0x1F]; 246 encoded_buf[2] = b32_hex_table[(quantum >> 25) & 0x1F]; 247 if (encoded_size == 0) encoded_size = 4; 248 case 1: 249 encoded_buf[1] = b32_hex_table[(quantum >> 30) & 0x1F]; 250 encoded_buf[0] = b32_hex_table[(quantum >> 35) & 0x1F]; 251 if (encoded_size == 0) encoded_size = 2; 252 } 253 254 if (!no_padding) { 255 // Fill the padding with '='. 256 for (size_t i = encoded_size; i < sizeof(encoded_buf); i++) { 257 encoded_buf[i] = '='; 258 } 259 260 encoded_size = sizeof(encoded_buf); 261 } 262 263 // Move the current encoded string chunk to the returned buffer. 264 memcpy(encoded_str_ptr, encoded_buf, encoded_size); 265 encoded_str_ptr += encoded_size; 266 } 267 } 268 269 #else 270 271 extern int _this_declaration_avoids_iso_c_empty_translation_unit_warning; 272 273 #endif // MDNSRESPONDER_SUPPORTS(APPLE, DNSSECv2) 274