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/CommonDigest.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 #if !TARGET_OS_OSX 39 static SecKeyRef 40 create_public_sec_key(const dns_rr_t *const key_record); 41 42 static CFDataRef 43 create_data_to_verify(dns_wire_t *const message, const dns_rr_t *const signature); 44 #endif // !TARGET_OS_OSX 45 46 bool 47 srp_sig0_verify(dns_wire_t *message, dns_rr_t *key, dns_rr_t *signature) 48 { 49 #if !TARGET_OS_OSX 50 bool valid = false; 51 CFErrorRef cf_error = NULL; 52 SecKeyRef public_key = NULL; 53 CFDataRef data_to_verify_cfdata = NULL; 54 CFDataRef sig_to_match_cfdata = NULL; 55 56 // The algorithm in KEY and SIG(0) has to match. 57 require_action_quiet(key->data.key.algorithm == signature->data.sig.algorithm, exit, 58 ERROR("KEY algorithm does not match the SIG(0) algorithm - " 59 "KEY algorithm: %u, SIG(0) algorithm: %u", 60 key->data.key.algorithm, signature->data.sig.algorithm)); 61 62 // The only supported algorithm now is ECDSA Curve P-256 with SHA-256. 63 require_action_quiet(signature->data.sig.algorithm == dnssec_keytype_ecdsa, exit, 64 ERROR("Unsupported KEY algorithm - KEY algorithm: %u", signature->data.sig.algorithm)); 65 66 // The key size should always be ECDSA_KEY_SIZE since only ECDSA Curve P-256 with SHA-256 is used now. 67 require_action_quiet(key->data.key.len == ECDSA_KEY_SIZE, exit, 68 ERROR("Invalid KEY length - KEY len: %d", key->data.key.len)); 69 70 // The signature size should always be ECDSA_SHA256_SIG_SIZE, since only ECDSA Curve P-256 with SHA-256 is used now. 71 require_action_quiet(signature->data.sig.len == ECDSA_SHA256_SIG_SIZE, exit, 72 ERROR("Invalid SIG(0) length - SIG(0) length: %d", signature->data.sig.len)); 73 74 // Get SecKeyRef given the KEY data. 75 public_key = create_public_sec_key(key); 76 require_action_quiet(public_key != NULL, exit, ERROR("Failed to create public_key")); 77 78 // Create signature to check. 79 sig_to_match_cfdata = CFDataCreate(kCFAllocatorDefault, signature->data.sig.signature, signature->data.sig.len); 80 require_action_quiet(sig_to_match_cfdata != NULL, exit, 81 ERROR("CFDataCreate failed when creating sig_to_match_cfdata")); 82 83 // Reconstruct the data (the digest of the raw data) that is signed by SIG(0). 84 data_to_verify_cfdata = create_data_to_verify(message, signature); 85 require_action_quiet(data_to_verify_cfdata != NULL, exit, ERROR("Failed to data_to_verify_cfdata")); 86 87 // Set the corresponding SecKeyAlgorithm for SecKeyVerifySignature. 88 SecKeyAlgorithm verify_algorithm; 89 switch (key->data.key.algorithm) { 90 case dnssec_keytype_ecdsa: 91 verify_algorithm = kSecKeyAlgorithmECDSASignatureDigestRFC4754; 92 break; 93 94 default: 95 FAULT("Unsupported KEY algorithm - KEY algorithm: %u", key->data.key.algorithm); 96 goto exit; 97 } 98 99 // Validate the signature. 100 valid = SecKeyVerifySignature(public_key, verify_algorithm, data_to_verify_cfdata, sig_to_match_cfdata, &cf_error); 101 if (!valid) { 102 char errbuf[200]; 103 CFStringRef error_cfstring = CFErrorCopyDescription(cf_error); 104 CFStringGetCString(error_cfstring, errbuf, sizeof(errbuf), kCFStringEncodingUTF8); 105 ERROR("SecKeyVerifySignature failed to validate - Error Description: %s", errbuf); 106 CFRelease(error_cfstring); 107 CFRelease(cf_error); 108 cf_error = NULL; 109 } 110 111 exit: 112 if (data_to_verify_cfdata != NULL) { 113 CFRelease(data_to_verify_cfdata); 114 } 115 if (sig_to_match_cfdata != NULL) { 116 CFRelease(sig_to_match_cfdata); 117 } 118 if (public_key != NULL) { 119 CFRelease(public_key); 120 } 121 122 return valid; 123 #else 124 (void)message; 125 (void)key; 126 (void)signature; 127 128 return true; 129 #endif // !TARGET_OS_OSX 130 } 131 132 #if !TARGET_OS_OSX 133 static SecKeyRef 134 create_public_sec_key(const dns_rr_t *const key_record) 135 { 136 SecKeyRef key_ref = NULL; 137 CFErrorRef cf_error = NULL; 138 if (key_record->data.key.algorithm == dnssec_keytype_ecdsa){ 139 uint8_t four = 4; 140 const void *public_key_options_keys[] = {kSecAttrKeyType, kSecAttrKeyClass}; 141 const void *public_key_options_values[] = {kSecAttrKeyTypeECSECPrimeRandom, kSecAttrKeyClassPublic}; 142 CFMutableDataRef public_key_cfdata = NULL; 143 CFDictionaryRef public_key_options = NULL; 144 145 // The format of the public key data is 04 | X | Y 146 public_key_cfdata = CFDataCreateMutable(kCFAllocatorDefault, 1 + key_record->data.key.len); 147 require_action_quiet(public_key_cfdata != NULL, ecdsa_exit, 148 ERROR("CFDataCreateMutable failed when creating public key CFMutableDataRef")); 149 CFDataAppendBytes(public_key_cfdata, &four, sizeof(four)); 150 CFDataAppendBytes(public_key_cfdata, key_record->data.key.key, key_record->data.key.len); 151 152 public_key_options = CFDictionaryCreate(kCFAllocatorDefault, public_key_options_keys, public_key_options_values, 153 sizeof(public_key_options_keys) / sizeof(void *), 154 &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); 155 require_action_quiet(public_key_options != NULL, ecdsa_exit, 156 ERROR("CFDictionaryCreate failed when creating public key options CFDictionaryRef")); 157 158 key_ref = SecKeyCreateWithData(public_key_cfdata, public_key_options, &cf_error); 159 require_action_quiet(key_ref != NULL, ecdsa_exit, 160 ERROR("SecKeyCreateWithData failed when creating public key SecKeyRef")); 161 162 ecdsa_exit: 163 if (public_key_cfdata != NULL) { 164 CFRelease(public_key_cfdata); 165 } 166 if (public_key_options != NULL) { 167 CFRelease(public_key_options); 168 } 169 } else { 170 key_ref = NULL; 171 } 172 173 if (cf_error != NULL) { 174 CFRelease(cf_error); 175 cf_error = NULL; 176 } 177 return key_ref; 178 } 179 180 static CFDataRef 181 create_data_to_verify(dns_wire_t *const message, const dns_rr_t *const signature) 182 { 183 bool encounter_error; 184 CFDataRef data_to_verify_cfdata = NULL; 185 uint8_t *canonical_signer_name = NULL; 186 uint8_t digest[ECDSA_SHA256_HASH_SIZE]; 187 188 // Right now, the only supported KEY algorithm is ECDSAP256. 189 require_action_quiet(signature->data.sig.algorithm == dnssec_keytype_ecdsa, exit, encounter_error = true; 190 FAULT("Unsupported SIG(0) algorithm - SIG(0) algorithm: %u", signature->data.sig.algorithm)); 191 192 CC_SHA256_CTX cc_digest_context; 193 CC_SHA256_Init(&cc_digest_context); 194 195 // data to be hashed = (SIG(0) RDATA without signature field) + (request - SIG(0)). 196 197 // (SIG(0) RDATA without signature field) = SIG(0) fields without signer name + canonical signer name. 198 // Copy SIG(0) fields without signer name. 199 CC_SHA256_Update(&cc_digest_context, &message->data[signature->data.sig.start + SIG_HEADERLEN], SIG_STATIC_RDLEN); 200 201 // Construct and copy canonical signer name. 202 size_t canonical_signer_name_length = dns_name_wire_length(signature->data.sig.signer); 203 // CC_SHA256_Update only accepts CC_LONG type (which is uint32_t) length parameter, so we need to check if the 204 // canonical_signer_name_length has invalid value. 205 require_action_quiet(canonical_signer_name_length <= MAXDOMNAMELEN, exit, encounter_error = true; 206 FAULT("Invalid signer name length - signer name length: %zu", canonical_signer_name_length)); 207 208 canonical_signer_name = malloc(canonical_signer_name_length); 209 require_action_quiet(canonical_signer_name != NULL, exit, encounter_error = true; 210 ERROR("malloc failed when allocating memory - for canonical_signer_name, len: %lu", 211 canonical_signer_name_length)); 212 213 bool convert_fail = !dns_name_to_wire_canonical(canonical_signer_name, canonical_signer_name_length, 214 signature->data.sig.signer); 215 require_action_quiet(!convert_fail, exit, encounter_error = true; 216 ERROR("Failed to write canonical name - canonical_signer_name_length: %lu", 217 canonical_signer_name_length)); 218 219 CC_SHA256_Update(&cc_digest_context, canonical_signer_name, (CC_LONG)canonical_signer_name_length); 220 221 // Copy (request - SIG(0)). 222 // The authority response count is before the counts have been adjusted for the inclusion of the SIG(0). 223 message->arcount = htons(ntohs(message->arcount) - 1); 224 CC_SHA256_Update(&cc_digest_context, (uint8_t *)message, offsetof(dns_wire_t, data) + signature->data.sig.start); 225 // Recover the count after copying. 226 message->arcount = htons(ntohs(message->arcount) + 1); 227 228 // Generate the final digest. 229 CC_SHA256_Final(digest, &cc_digest_context); 230 231 // Create CFDataRef. 232 data_to_verify_cfdata = CFDataCreate(kCFAllocatorDefault, digest, sizeof(digest)); 233 require_action_quiet(data_to_verify_cfdata != NULL, exit, encounter_error = true; 234 ERROR("CFDataCreate failed when creating data_to_verify_cfdata")); 235 236 encounter_error = false; 237 exit: 238 if (canonical_signer_name != NULL) { 239 free(canonical_signer_name); 240 } 241 if (encounter_error) { 242 if (data_to_verify_cfdata != NULL) { 243 CFRelease(data_to_verify_cfdata); 244 } 245 } 246 return data_to_verify_cfdata; 247 } 248 #endif // !TARGET_OS_OSX 249 250 //====================================================================================================================== 251 252 #if SECTRANSFORM_AVAILABLE 253 // Function to copy out the public key as binary data 254 void 255 srp_print_key(srp_key_t *key) 256 { 257 SecTransformRef b64encoder; 258 CFDataRef key_data, public_key = NULL; 259 CFDataRef encoded; 260 const uint8_t *data; 261 CFErrorRef error = NULL; 262 263 key_data = SecKeyCopyExternalRepresentation(key->public, &error); 264 if (error == NULL) { 265 data = CFDataGetBytePtr(key_data); 266 public_key = CFDataCreateWithBytesNoCopy(NULL, data + 1, 267 CFDataGetLength(key_data) - 1, kCFAllocatorNull); 268 if (public_key != NULL) { 269 b64encoder = SecEncodeTransformCreate(public_key, &error); 270 if (error == NULL) { 271 SecTransformSetAttribute(b64encoder, kSecTransformInputAttributeName, key, &error); 272 if (error == NULL) { 273 encoded = SecTransformExecute(b64encoder, &error); 274 if (error == NULL) { 275 data = CFDataGetBytePtr(encoded); 276 fputs("thread-demo.default.service.arpa. IN KEY 513 3 13 ", stdout); 277 fwrite(data, CFDataGetLength(encoded), 1, stdout); 278 putc('\n', stdout); 279 } 280 if (encoded != NULL) { 281 CFRelease(encoded); 282 } 283 } 284 } 285 if (b64encoder != NULL) { 286 CFRelease(b64encoder); 287 } 288 CFRelease(public_key); 289 } 290 } 291 if (key_data != NULL) { 292 CFRelease(key_data); 293 } 294 } 295 #endif // SECTRANSFORM_AVAILABLE 296 297 // Local Variables: 298 // mode: C 299 // tab-width: 4 300 // c-file-style: "bsd" 301 // c-basic-offset: 4 302 // fill-column: 108 303 // indent-tabs-mode: nil 304 // End: 305