3 * Copyright (c) 2019 Apple Computer, Inc. All rights reserved.
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
9 * http://www.apache.org/licenses/LICENSE-2.0
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.
17 * DNS SIG(0) signature verification for DNSSD SRP using MacOS Security Framework.
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
25 #include <arpa/inet.h>
28 #include <CommonCrypto/CommonDigestSPI.h>
29 #include <AssertMacros.h>
32 #define SRP_CRYPTO_MACOS_INTERNAL
34 #include "srp-crypto.h"
36 //======================================================================================================================
39 create_public_sec_key(const dns_rr_t
*const key_record
);
42 create_data_to_verify(dns_wire_t
*const message
, const dns_rr_t
*const signature
);
45 srp_sig0_verify(dns_wire_t
*message
, dns_rr_t
*key
, dns_rr_t
*signature
)
48 CFErrorRef cf_error
= NULL
;
49 SecKeyRef public_key
= NULL
;
50 CFDataRef data_to_verify_cfdata
= NULL
;
51 CFDataRef sig_to_match_cfdata
= NULL
;
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
));
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
));
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
));
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
));
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"));
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"));
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"));
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
;
92 FAULT("Unsupported KEY algorithm - KEY algorithm: %u", key
->data
.key
.algorithm
);
96 // Validate the signature.
97 valid
= SecKeyVerifySignature(public_key
, verify_algorithm
, data_to_verify_cfdata
, sig_to_match_cfdata
, &cf_error
);
99 CFStringRef error_cfstring
= CFErrorCopyDescription(cf_error
);
100 ERROR("SecKeyVerifySignature failed to validate - Error Description: %@", error_cfstring
);
101 CFRelease(error_cfstring
);
107 if (data_to_verify_cfdata
!= NULL
) {
108 CFRelease(data_to_verify_cfdata
);
110 if (sig_to_match_cfdata
!= NULL
) {
111 CFRelease(sig_to_match_cfdata
);
113 if (public_key
!= NULL
) {
114 CFRelease(public_key
);
121 create_public_sec_key(const dns_rr_t
*const key_record
)
123 SecKeyRef key_ref
= NULL
;
124 CFErrorRef cf_error
= NULL
;
125 if (key_record
->data
.key
.algorithm
== dnssec_keytype_ecdsa
){
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
;
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
);
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"));
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"));
150 if (public_key_cfdata
!= NULL
) {
151 CFRelease(public_key_cfdata
);
153 if (public_key_options
!= NULL
) {
154 CFRelease(public_key_options
);
160 if (cf_error
!= NULL
) {
168 create_data_to_verify(dns_wire_t
*const message
, const dns_rr_t
*const signature
)
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
;
178 switch (signature
->data
.sig
.algorithm
) {
179 case dnssec_keytype_ecdsa
:
180 cc_digest_algorithm
= kCCDigestSHA256
;
181 digest_length
= ECDSA_SHA256_HASH_SIZE
;
185 encounter_error
= true;
186 FAULT("Unsupported SIG(0) algorithm - SIG(0) algorithm: %u", signature
->data
.sig
.algorithm
);
189 CCDigestCtx cc_digest_context
;
190 CCDigestInit(cc_digest_algorithm
, &cc_digest_context
);
192 // data to be hashed = (SIG(0) RDATA without signature field) + (request - SIG(0)).
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
);
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
));
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
));
211 CCDigestUpdate(&cc_digest_context
, canonical_signer_name
, canonical_signer_name_length
);
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);
220 // Generate the final digest.
221 CCDigestFinal(&cc_digest_context
, digest
);
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"));
228 encounter_error
= false;
230 if (canonical_signer_name
!= NULL
) {
231 free(canonical_signer_name
);
233 if (encounter_error
) {
234 if (data_to_verify_cfdata
!= NULL
) {
235 CFRelease(data_to_verify_cfdata
);
238 return data_to_verify_cfdata
;
241 //======================================================================================================================
243 #if SECTRANSFORM_AVAILABLE
244 // Function to copy out the public key as binary data
246 srp_print_key(srp_key_t
*key
)
248 SecTransformRef b64encoder
;
249 CFDataRef key_data
, public_key
= NULL
;
252 CFErrorRef error
= NULL
;
254 key_data
= SecKeyCopyExternalRepresentation(key
->public, &error
);
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
);
262 SecTransformSetAttribute(b64encoder
, kSecTransformInputAttributeName
, key
, &error
);
264 encoded
= SecTransformExecute(b64encoder
, &error
);
266 data
= CFDataGetBytePtr(encoded
);
267 fputs("thread-demo.default.service.arpa. IN KEY 513 3 13 ", stdout
);
268 fwrite(data
, CFDataGetLength(encoded
), 1, stdout
);
271 if (encoded
!= NULL
) {
276 if (b64encoder
!= NULL
) {
277 CFRelease(b64encoder
);
279 CFRelease(public_key
);
282 if (key_data
!= NULL
) {
286 #endif // SECTRANSFORM_AVAILABLE
291 // c-file-style: "bsd"
294 // indent-tabs-mode: nil