Home | History | Annotate | Line # | Download | only in ServiceRegistration
      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