5 // Copyright (c) 2020 Apple Inc. All rights reserved.
8 #pragma mark - Includes
10 #if MDNSRESPONDER_SUPPORTS(APPLE, DNSSECv2)
12 #include <AssertMacros.h>
13 #include "dnssec_v2_log.h"
15 #pragma mark - Constants
19 #pragma mark b64_table
20 static const char b64_table
[] = {
21 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H',
22 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P',
23 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X',
24 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f',
25 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n',
26 'o', 'p', 'q', 'r', 's', 't', 'u', 'v',
27 'w', 'x', 'y', 'z', '0', '1', '2', '3',
28 '4', '5', '6', '7', '8', '9', '+', '/'
31 #pragma mark b32_hex_table
32 static const char b32_hex_table
[] = {
33 '0', '1', '2', '3', '4', '5', '6', '7',
34 '8', '9', 'A', 'B', 'C', 'D', 'E', 'F',
35 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N',
36 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V'
43 #define MAX_LENGTH_B64_ENCODING_DATA (SIZE_MAX * 3 / 4)
44 #define MAX_LENGTH_B32_HEX_ENCODING_DATA (SIZE_MAX * 5 / 8)
46 #pragma mark - Functions
50 #pragma mark - base_n_encode
52 base_n_encode(base_n_t base_n
, const unsigned char * const _Nonnull data
, size_t data_len
) {
53 return base_n_encode_ex(base_n
, data
, data_len
, NULL
);
56 #pragma mark - base_n_encode_ex
58 base_n_encode_ex(base_n_t base_n
, const unsigned char * const _Nonnull data
, size_t data_len
, size_t * const _Nullable out_str_len
) {
59 char * encoded_str
= mDNSNULL
;
60 const size_t encoded_str_len
= get_base_n_encoded_str_length(base_n
, data_len
);
61 char * encoded_str_ptr
;
62 const unsigned char * data_ptr
= data
;
63 const unsigned char * const data_ptr_limit
= data_ptr
+ data_len
;
67 encoded_str
= malloc(encoded_str_len
+ 1);
68 require_quiet(encoded_str
!= mDNSNULL
, exit
);
70 // ensure that the string always ends with '\0'
71 encoded_str
[encoded_str_len
] = '\0';
72 encoded_str_ptr
= encoded_str
;
74 if (out_str_len
!= mDNSNULL
) {
75 *out_str_len
= encoded_str_len
;
80 case DNSSEC_BASE_64
: {
81 for(; data_ptr
< data_ptr_limit
;) {
83 size_t encoded_size
= 0;
85 remain
= data_ptr_limit
- data_ptr
;
87 // get 24 bits from 3 bytes
90 case 3: quantum
|= (uint64_t)data_ptr
[2]; // Bits 16 - 23
91 case 2: quantum
|= ((uint64_t)data_ptr
[1]) << 8; // Bits 8 - 15
92 case 1: quantum
|= ((uint64_t)data_ptr
[0]) << 16; // Bits 0 - 7
94 // advance the data pointer
95 data_ptr
+= ((remain
> 3) ? 3 : remain
);
97 // convert 24 bits to 4 characters
101 encoded_buf
[3] = b64_table
[ quantum
& 0x3F];
104 encoded_buf
[2] = b64_table
[(quantum
>> 6) & 0x3F];
105 if (encoded_size
== 0) encoded_size
= 3;
107 encoded_buf
[1] = b64_table
[(quantum
>> 12) & 0x3F];
108 encoded_buf
[0] = b64_table
[(quantum
>> 18) & 0x3F];
109 if (encoded_size
== 0) encoded_size
= 2;
112 // fill the padding with '='
113 for (size_t i
= encoded_size
; i
< sizeof(encoded_buf
); i
++) {
114 encoded_buf
[i
] = '=';
117 // move the current encoded string chunk to the returned buffer
118 memcpy(encoded_str_ptr
, encoded_buf
, sizeof(encoded_buf
));
119 encoded_str_ptr
+= sizeof(encoded_buf
);
123 case DNSSEC_BASE_32_HEX
: {
124 for(; data_ptr
< data_ptr_limit
;) {
126 size_t encoded_size
= 0;
128 remain
= data_ptr_limit
- data_ptr
;
130 // get 40 bits from 8 bytes
133 case 5: quantum
|= (uint64_t)data_ptr
[4]; // Bits 32 - 39
134 case 4: quantum
|= ((uint64_t)data_ptr
[3]) << 8; // Bits 24 - 32
135 case 3: quantum
|= ((uint64_t)data_ptr
[2]) << 16; // Bits 16 - 23
136 case 2: quantum
|= ((uint64_t)data_ptr
[1]) << 24; // Bits 8 - 15
137 case 1: quantum
|= ((uint64_t)data_ptr
[0]) << 32; // Bits 0 - 7
139 // advance the data pointer
140 data_ptr
+= ((remain
> 5) ? 5 : remain
);
142 // convert 40 bits to 8 characters
146 encoded_buf
[7] = b32_hex_table
[quantum
& 0x1F];
149 encoded_buf
[6] = b32_hex_table
[(quantum
>> 5) & 0x1F];
150 encoded_buf
[5] = b32_hex_table
[(quantum
>> 10) & 0x1F];
151 if (encoded_size
== 0) encoded_size
= 7;
153 encoded_buf
[4] = b32_hex_table
[(quantum
>> 15) & 0x1F];
154 if (encoded_size
== 0) encoded_size
= 5;
156 encoded_buf
[3] = b32_hex_table
[(quantum
>> 20) & 0x1F];
157 encoded_buf
[2] = b32_hex_table
[(quantum
>> 25) & 0x1F];
158 if (encoded_size
== 0) encoded_size
= 4;
160 encoded_buf
[1] = b32_hex_table
[(quantum
>> 30) & 0x1F];
161 encoded_buf
[0] = b32_hex_table
[(quantum
>> 35) & 0x1F];
162 if (encoded_size
== 0) encoded_size
= 2;
165 // fill the padding with '='
166 for (size_t i
= encoded_size
; i
< sizeof(encoded_buf
); i
++) {
167 encoded_buf
[i
] = '=';
170 // move the current encoded string chunk to the returned buffer
171 memcpy(encoded_str_ptr
, encoded_buf
, sizeof(encoded_buf
));
172 encoded_str_ptr
+= sizeof(encoded_buf
);
177 // Unsupported Base N, returns empty string.
178 encoded_str
[0] = '\0';
186 #pragma mark - get_base_n_encoded_str_length
188 get_base_n_encoded_str_length(base_n_t base_n
, size_t data_len
) {
189 size_t encoded_str_len
= 0;
193 verify_action(data_len
<= MAX_LENGTH_B64_ENCODING_DATA
, data_len
= MAX_LENGTH_B64_ENCODING_DATA
);
194 encoded_str_len
= (data_len
+ 2) / 3 * 4;
196 case DNSSEC_BASE_32_HEX
:
197 verify_action(data_len
<= MAX_LENGTH_B32_HEX_ENCODING_DATA
, data_len
= MAX_LENGTH_B32_HEX_ENCODING_DATA
);
198 encoded_str_len
= (data_len
+ 4) / 5 * 8;
205 return encoded_str_len
;
208 #endif // MDNSRESPONDER_SUPPORTS(APPLE, DNSSECv2)