]> git.saurik.com Git - apple/mdnsresponder.git/blob - ServiceRegistration/verify-macos.c
mDNSResponder-1310.40.42.tar.gz
[apple/mdnsresponder.git] / ServiceRegistration / verify-macos.c
1 /* verify_macos.c
2 *
3 * Copyright (c) 2019 Apple Computer, Inc. All rights reserved.
4 *
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
8 *
9 * http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 *
17 * DNS SIG(0) signature verification for DNSSD SRP using MacOS Security Framework.
18 *
19 * Provides functions for generating a public key validating context based on SIG(0) KEY RR data, and
20 * validating a signature using a context generated with that public key. Currently only ECDSASHA256 is
21 * supported.
22 */
23
24 #include <stdio.h>
25 #include <arpa/inet.h>
26 #include <string.h>
27 #include <stdlib.h>
28 #include <CommonCrypto/CommonDigestSPI.h>
29 #include <AssertMacros.h>
30
31 #include "srp.h"
32 #define SRP_CRYPTO_MACOS_INTERNAL
33 #include "dns-msg.h"
34 #include "srp-crypto.h"
35
36 //======================================================================================================================
37
38 static SecKeyRef
39 create_public_sec_key(const dns_rr_t *const key_record);
40
41 static CFDataRef
42 create_data_to_verify(dns_wire_t *const message, const dns_rr_t *const signature);
43
44 bool
45 srp_sig0_verify(dns_wire_t *message, dns_rr_t *key, dns_rr_t *signature)
46 {
47 bool valid = false;
48 CFErrorRef cf_error = NULL;
49 SecKeyRef public_key = NULL;
50 CFDataRef data_to_verify_cfdata = NULL;
51 CFDataRef sig_to_match_cfdata = NULL;
52
53 // The algorithm in KEY and SIG(0) has to match.
54 require_action_quiet(key->data.key.algorithm == signature->data.sig.algorithm, exit,
55 ERROR("KEY algorithm does not match the SIG(0) algorithm - "
56 "KEY algorithm: %u, SIG(0) algorithm: %u",
57 key->data.key.algorithm, signature->data.sig.algorithm));
58
59 // The only supported algorithm now is ECDSA Curve P-256 with SHA-256.
60 require_action_quiet(signature->data.sig.algorithm == dnssec_keytype_ecdsa, exit,
61 ERROR("Unsupported KEY algorithm - KEY algorithm: %u", signature->data.sig.algorithm));
62
63 // The key size should always be ECDSA_KEY_SIZE since only ECDSA Curve P-256 with SHA-256 is used now.
64 require_action_quiet(key->data.key.len == ECDSA_KEY_SIZE, exit,
65 ERROR("Invalid KEY length - KEY len: %d", key->data.key.len));
66
67 // The signature size should always be ECDSA_SHA256_SIG_SIZE, since only ECDSA Curve P-256 with SHA-256 is used now.
68 require_action_quiet(signature->data.sig.len == ECDSA_SHA256_SIG_SIZE, exit,
69 ERROR("Invalid SIG(0) length - SIG(0) length: %d", signature->data.sig.len));
70
71 // Get SecKeyRef given the KEY data.
72 public_key = create_public_sec_key(key);
73 require_action_quiet(public_key != NULL, exit, ERROR("Failed to create public_key"));
74
75 // Create signature to check.
76 sig_to_match_cfdata = CFDataCreate(kCFAllocatorDefault, signature->data.sig.signature, signature->data.sig.len);
77 require_action_quiet(sig_to_match_cfdata != NULL, exit,
78 ERROR("CFDataCreate failed when creating sig_to_match_cfdata"));
79
80 // Reconstruct the data (the digest of the raw data) that is signed by SIG(0).
81 data_to_verify_cfdata = create_data_to_verify(message, signature);
82 require_action_quiet(data_to_verify_cfdata != NULL, exit, ERROR("Failed to data_to_verify_cfdata"));
83
84 // Set the corresponding SecKeyAlgorithm for SecKeyVerifySignature.
85 SecKeyAlgorithm verify_algorithm;
86 switch (key->data.key.algorithm) {
87 case dnssec_keytype_ecdsa:
88 verify_algorithm = kSecKeyAlgorithmECDSASignatureRFC4754;
89 break;
90
91 default:
92 FAULT("Unsupported KEY algorithm - KEY algorithm: %u", key->data.key.algorithm);
93 goto exit;
94 }
95
96 // Validate the signature.
97 valid = SecKeyVerifySignature(public_key, verify_algorithm, data_to_verify_cfdata, sig_to_match_cfdata, &cf_error);
98 if (!valid) {
99 CFStringRef error_cfstring = CFErrorCopyDescription(cf_error);
100 ERROR("SecKeyVerifySignature failed to validate - Error Description: %@", error_cfstring);
101 CFRelease(error_cfstring);
102 CFRelease(cf_error);
103 cf_error = NULL;
104 }
105
106 exit:
107 if (data_to_verify_cfdata != NULL) {
108 CFRelease(data_to_verify_cfdata);
109 }
110 if (sig_to_match_cfdata != NULL) {
111 CFRelease(sig_to_match_cfdata);
112 }
113 if (public_key != NULL) {
114 CFRelease(public_key);
115 }
116
117 return valid;
118 }
119
120 static SecKeyRef
121 create_public_sec_key(const dns_rr_t *const key_record)
122 {
123 SecKeyRef key_ref = NULL;
124 CFErrorRef cf_error = NULL;
125 if (key_record->data.key.algorithm == dnssec_keytype_ecdsa){
126 uint8_t four = 4;
127 const void *public_key_options_keys[] = {kSecAttrKeyType, kSecAttrKeyClass};
128 const void *public_key_options_values[] = {kSecAttrKeyTypeECSECPrimeRandom, kSecAttrKeyClassPublic};
129 CFMutableDataRef public_key_cfdata = NULL;
130 CFDictionaryRef public_key_options = NULL;
131
132 // The format of the public key data is 04 | X | Y
133 public_key_cfdata = CFDataCreateMutable(kCFAllocatorDefault, 1 + key_record->data.key.len);
134 require_action_quiet(public_key_cfdata != NULL, ecdsa_exit,
135 ERROR("CFDataCreateMutable failed when creating public key CFMutableDataRef"));
136 CFDataAppendBytes(public_key_cfdata, &four, sizeof(four));
137 CFDataAppendBytes(public_key_cfdata, key_record->data.key.key, key_record->data.key.len);
138
139 public_key_options = CFDictionaryCreate(kCFAllocatorDefault, public_key_options_keys, public_key_options_values,
140 sizeof(public_key_options_keys) / sizeof(void *),
141 &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
142 require_action_quiet(public_key_options != NULL, ecdsa_exit,
143 ERROR("CFDictionaryCreate failed when creating public key options CFDictionaryRef"));
144
145 key_ref = SecKeyCreateWithData(public_key_cfdata, public_key_options, &cf_error);
146 require_action_quiet(key_ref != NULL, ecdsa_exit,
147 ERROR("SecKeyCreateWithData failed when creating public key SecKeyRef"));
148
149 ecdsa_exit:
150 if (public_key_cfdata != NULL) {
151 CFRelease(public_key_cfdata);
152 }
153 if (public_key_options != NULL) {
154 CFRelease(public_key_options);
155 }
156 } else {
157 key_ref = NULL;
158 }
159
160 if (cf_error != NULL) {
161 CFRelease(cf_error);
162 cf_error = NULL;
163 }
164 return key_ref;
165 }
166
167 static CFDataRef
168 create_data_to_verify(dns_wire_t *const message, const dns_rr_t *const signature)
169 {
170 bool encounter_error;
171 CCDigestAlgorithm cc_digest_algorithm;
172 CFDataRef data_to_verify_cfdata = NULL;
173 uint8_t *canonical_signer_name = NULL;
174 #define MAX_HASH_SIZE ECDSA_SHA256_HASH_SIZE
175 uint8_t digest[MAX_HASH_SIZE];
176 size_t digest_length;
177
178 switch (signature->data.sig.algorithm) {
179 case dnssec_keytype_ecdsa:
180 cc_digest_algorithm = kCCDigestSHA256;
181 digest_length = ECDSA_SHA256_HASH_SIZE;
182 break;
183
184 default:
185 encounter_error = true;
186 FAULT("Unsupported SIG(0) algorithm - SIG(0) algorithm: %u", signature->data.sig.algorithm);
187 goto exit;
188 }
189 CCDigestCtx cc_digest_context;
190 CCDigestInit(cc_digest_algorithm, &cc_digest_context);
191
192 // data to be hashed = (SIG(0) RDATA without signature field) + (request - SIG(0)).
193
194 // (SIG(0) RDATA without signature field) = SIG(0) fields without signer name + canonical signer name.
195 // Copy SIG(0) fields without signer name.
196 CCDigestUpdate(&cc_digest_context, &message->data[signature->data.sig.start + SIG_HEADERLEN], SIG_STATIC_RDLEN);
197
198 // Construct and copy canonical signer name.
199 size_t canonical_signer_name_length = dns_name_wire_length(signature->data.sig.signer);
200 canonical_signer_name = malloc(canonical_signer_name_length);
201 require_action_quiet(canonical_signer_name != NULL, exit, encounter_error = true;
202 ERROR("malloc failed when allocating memory - for canonical_signer_name, len: %lu",
203 canonical_signer_name_length));
204
205 bool convert_fail = !dns_name_to_wire_canonical(canonical_signer_name, canonical_signer_name_length,
206 signature->data.sig.signer);
207 require_action_quiet(!convert_fail, exit, encounter_error = true;
208 ERROR("Failed to write canonical name - canonical_signer_name_length: %lu",
209 canonical_signer_name_length));
210
211 CCDigestUpdate(&cc_digest_context, canonical_signer_name, canonical_signer_name_length);
212
213 // Copy (request - SIG(0)).
214 // The authority response count is before the counts have been adjusted for the inclusion of the SIG(0).
215 message->arcount = htons(ntohs(message->arcount) - 1);
216 CCDigestUpdate(&cc_digest_context, (uint8_t *)message, offsetof(dns_wire_t, data) + signature->data.sig.start);
217 // Recover the count after copying.
218 message->arcount = htons(ntohs(message->arcount) + 1);
219
220 // Generate the final digest.
221 CCDigestFinal(&cc_digest_context, digest);
222
223 // Create CFDataRef.
224 data_to_verify_cfdata = CFDataCreate(kCFAllocatorDefault, digest, digest_length);
225 require_action_quiet(data_to_verify_cfdata != NULL, exit, encounter_error = true;
226 ERROR("CFDataCreate failed when creating data_to_verify_cfdata"));
227
228 encounter_error = false;
229 exit:
230 if (canonical_signer_name != NULL) {
231 free(canonical_signer_name);
232 }
233 if (encounter_error) {
234 if (data_to_verify_cfdata != NULL) {
235 CFRelease(data_to_verify_cfdata);
236 }
237 }
238 return data_to_verify_cfdata;
239 }
240
241 //======================================================================================================================
242
243 #if SECTRANSFORM_AVAILABLE
244 // Function to copy out the public key as binary data
245 void
246 srp_print_key(srp_key_t *key)
247 {
248 SecTransformRef b64encoder;
249 CFDataRef key_data, public_key = NULL;
250 CFDataRef encoded;
251 const uint8_t *data;
252 CFErrorRef error = NULL;
253
254 key_data = SecKeyCopyExternalRepresentation(key->public, &error);
255 if (error == NULL) {
256 data = CFDataGetBytePtr(key_data);
257 public_key = CFDataCreateWithBytesNoCopy(NULL, data + 1,
258 CFDataGetLength(key_data) - 1, kCFAllocatorNull);
259 if (public_key != NULL) {
260 b64encoder = SecEncodeTransformCreate(public_key, &error);
261 if (error == NULL) {
262 SecTransformSetAttribute(b64encoder, kSecTransformInputAttributeName, key, &error);
263 if (error == NULL) {
264 encoded = SecTransformExecute(b64encoder, &error);
265 if (error == NULL) {
266 data = CFDataGetBytePtr(encoded);
267 fputs("thread-demo.default.service.arpa. IN KEY 513 3 13 ", stdout);
268 fwrite(data, CFDataGetLength(encoded), 1, stdout);
269 putc('\n', stdout);
270 }
271 if (encoded != NULL) {
272 CFRelease(encoded);
273 }
274 }
275 }
276 if (b64encoder != NULL) {
277 CFRelease(b64encoder);
278 }
279 CFRelease(public_key);
280 }
281 }
282 if (key_data != NULL) {
283 CFRelease(key_data);
284 }
285 }
286 #endif // SECTRANSFORM_AVAILABLE
287
288 // Local Variables:
289 // mode: C
290 // tab-width: 4
291 // c-file-style: "bsd"
292 // c-basic-offset: 4
293 // fill-column: 108
294 // indent-tabs-mode: nil
295 // End: