]> git.saurik.com Git - apple/mdnsresponder.git/blob - ServiceRegistration/sign-macos.c
mDNSResponder-1310.40.42.tar.gz
[apple/mdnsresponder.git] / ServiceRegistration / sign-macos.c
1 /* sign.c
2 *
3 * Copyright (c) 2018-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 generation for DNSSD SRP using mbedtls.
18 *
19 * Functions required for loading, saving, and generating public/private keypairs, extracting the public key
20 * into KEY RR data, and computing signatures.
21 *
22 * This is the implementation for Mac OS X.
23 */
24
25
26 #include <stdio.h>
27 #include <arpa/inet.h>
28 #include <sys/random.h>
29 #include <string.h>
30 #include <stdlib.h>
31 #include <unistd.h>
32 #include <fcntl.h>
33 #include <errno.h>
34
35 #include <dns_sd.h>
36
37 #include "srp.h"
38 #include "srp-api.h"
39 #include "dns-msg.h"
40 #define SRP_CRYPTO_MACOS_INTERNAL
41 #include "srp-crypto.h"
42
43 // Key is stored in an opaque data structure, for mbedtls this is an mbedtls_pk_context.
44 // Function to read a public key from a KEY record
45 // Function to validate a signature given some data and a public key (not required on client)
46
47 // Function to free a key
48 void
49 srp_keypair_free(srp_key_t *key)
50 {
51 free(key);
52 }
53
54 uint16_t
55 srp_random16()
56 {
57 return arc4random_uniform(65536);
58 }
59
60 static void
61 srp_sec_error_print(const char *reason, OSStatus status)
62 {
63 const char *utf8 = NULL;
64 CFStringRef err = SecCopyErrorMessageString(status, NULL);
65 if (err != NULL) {
66 utf8 = CFStringGetCStringPtr(err, kCFStringEncodingUTF8);
67 }
68 if (utf8 != NULL) {
69 ERROR(PUB_S_SRP ": " PUB_S_SRP, reason, utf8);
70 } else {
71 ERROR(PUB_S_SRP ": %d", reason, (int)status);
72 }
73 if (err != NULL) {
74 CFRelease(err);
75 }
76 }
77
78 // Function to generate a key
79 static srp_key_t *
80 srp_get_key_internal(const char *key_name, bool delete)
81 {
82 long two56 = 256;
83 srp_key_t *key = NULL;
84 OSStatus status;
85
86 CFMutableDictionaryRef key_parameters = CFDictionaryCreateMutable(NULL, 8,
87 &kCFTypeDictionaryKeyCallBacks,
88 &kCFTypeDictionaryValueCallBacks);
89 CFMutableDictionaryRef pubkey_parameters;
90
91 if (key_parameters != NULL) {
92 CFDictionaryAddValue(key_parameters, kSecAttrIsPermanent, kCFBooleanTrue);
93 CFDictionaryAddValue(key_parameters, kSecAttrKeyType, kSecAttrKeyTypeECSECPrimeRandom);
94 CFNumberRef num = CFNumberCreate(NULL, kCFNumberLongType, &two56);
95 CFDictionaryAddValue(key_parameters, kSecAttrKeySizeInBits, num);
96 CFRelease(num);
97 CFStringRef str = CFStringCreateWithCString(NULL, key_name, kCFStringEncodingUTF8);
98 CFDictionaryAddValue(key_parameters, kSecAttrLabel, str);
99 CFRelease(str);
100 CFDictionaryAddValue(key_parameters, kSecReturnRef, kCFBooleanTrue);
101 CFDictionaryAddValue(key_parameters, kSecMatchLimit, kSecMatchLimitOne);
102 CFDictionaryAddValue(key_parameters, kSecClass, kSecClassKey);
103 pubkey_parameters = CFDictionaryCreateMutableCopy(NULL, 8, key_parameters);
104 if (pubkey_parameters != NULL) {
105 CFDictionaryAddValue(key_parameters, kSecAttrKeyClass, kSecAttrKeyClassPrivate);
106 CFDictionaryAddValue(pubkey_parameters, kSecAttrKeyClass, kSecAttrKeyClassPublic);
107 CFDictionaryAddValue(pubkey_parameters, kSecAttrIsExtractable, kCFBooleanTrue);
108 CFDictionaryAddValue(key_parameters, kSecAttrIsExtractable, kCFBooleanTrue);
109 #if !defined(OPEN_SOURCE) && TARGET_OS_TV
110 CFDictionaryAddValue(pubkey_parameters, kSecAttrAccessible,
111 kSecAttrAccessibleAlwaysThisDeviceOnlyPrivate);
112 #else
113 CFDictionaryAddValue(pubkey_parameters, kSecAttrAccessible,
114 kSecAttrAccessibleAfterFirstUnlockThisDeviceOnly);
115 #endif
116 if (delete) {
117 status = SecItemDelete(key_parameters);
118 if (status == errSecSuccess) {
119 status = SecItemDelete(pubkey_parameters);
120 }
121 key = NULL;
122 } else {
123 key = calloc(1, sizeof(*key));
124
125 if (key != NULL) {
126 CFErrorRef error = NULL;
127
128 // See if the key is already on the keychain.
129 status = SecItemCopyMatching(key_parameters, (CFTypeRef *)&key->private);
130 if (status == errSecSuccess) {
131 status = SecItemCopyMatching(pubkey_parameters, (CFTypeRef *)&key->public);
132 } else {
133 key->private = SecKeyCreateRandomKey(key_parameters, &error);
134 if (key->private != NULL) {
135 key->public = SecKeyCopyPublicKey(key->private);
136 }
137 }
138 if (key->public == NULL || key->private == NULL) {
139 if (error != NULL) {
140 CFShow(error);
141 } else {
142 srp_sec_error_print("Failed to get key pair", status);
143 }
144 free(key);
145 key = NULL;
146 }
147 }
148 }
149 CFRelease(key_parameters);
150 CFRelease(pubkey_parameters);
151 }
152 }
153 return key;
154 }
155
156 srp_key_t *
157 srp_get_key(const char *key_name, void *__unused os_context)
158 {
159 return srp_get_key_internal(key_name, false);
160 }
161
162 // Remove an existing key
163 int
164 srp_reset_key(const char *key_name, void *__unused os_context)
165 {
166 srp_get_key_internal(key_name, true);
167 return kDNSServiceErr_NoError;
168 }
169
170 // void to get the length of the public key
171 size_t
172 srp_pubkey_length(srp_key_t *key)
173 {
174 (void)key;
175 return ECDSA_KEY_SIZE;
176 }
177
178 int
179 srp_key_algorithm(srp_key_t *key)
180 {
181 (void)key;
182 return dnssec_keytype_ecdsa;
183 }
184
185 size_t
186 srp_signature_length(srp_key_t *key)
187 {
188 (void)key;
189 return ECDSA_KEY_SIZE;
190 }
191
192 // Function to copy out the public key as binary data
193 int
194 srp_pubkey_copy(uint8_t *buf, size_t max, srp_key_t *key)
195 {
196 CFErrorRef error = NULL;
197 int ret = 0;
198 CFDataRef pubkey = SecKeyCopyExternalRepresentation(key->public, &error);
199 if (pubkey == NULL) {
200 if (error != NULL) {
201 CFShow(error);
202 } else {
203 ERROR("Unknown failure in SecKeyCopyExternalRepresentation");
204 }
205 } else {
206 const uint8_t *bytes = CFDataGetBytePtr(pubkey);
207 unsigned long len = CFDataGetLength(pubkey);
208
209 // Should be 04 | X | Y
210 if (bytes[0] != 4) {
211 ERROR("Unexpected preamble to public key: %d", bytes[0]);
212 } else if (len - 1 > max) {
213 ERROR("Not enough room for public key in buffer: %ld > %zd", len - 1, max);
214 } else if (len - 1 != ECDSA_KEY_SIZE) {
215 ERROR("Unexpected key size %ld", len - 1);
216 } else {
217 memcpy(buf, bytes + 1, len - 1);
218 ret = ECDSA_KEY_SIZE;
219 }
220 CFRelease(pubkey);
221 }
222 return ret;
223 }
224
225 // Function to generate a signature given some data and a private key
226 int
227 srp_sign(uint8_t *output, size_t max, uint8_t *message, size_t msglen,
228 uint8_t *rr, size_t rdlen, srp_key_t *key)
229 {
230 CFMutableDataRef payload = NULL;
231 CFDataRef signature = NULL;
232 CFErrorRef error = NULL;
233 const uint8_t *bytes;
234 unsigned long len;
235 int ret = 0;
236
237 typedef struct {
238 SecAsn1Item r, s;
239 } raw_signature_data_t;
240 raw_signature_data_t raw_signature;
241
242 ECDSA_SIG_TEMPLATE(sig_template);
243
244 if (max < ECDSA_SHA256_SIG_SIZE) {
245 ERROR("srp_sign: not enough space in output buffer (%lu) for signature (%d).",
246 (unsigned long)max, ECDSA_SHA256_SIG_SIZE);
247 return 0;
248 }
249
250 payload = CFDataCreateMutable(NULL, msglen + rdlen);
251 if (payload == NULL) {
252 ERROR("srp_sign: CFDataCreateMutable failed on length %zd", msglen + rdlen);
253 return 0;
254 }
255 CFDataAppendBytes(payload, rr, rdlen);
256 CFDataAppendBytes(payload, message, msglen);
257
258 signature = SecKeyCreateSignature(key->private,
259 kSecKeyAlgorithmECDSASignatureMessageX962SHA256, payload, &error);
260 CFRelease(payload);
261 if (error != NULL) {
262 CFRelease(signature);
263 CFShow(error);
264 return 0;
265 }
266 if (signature == NULL) {
267 ERROR("No error, but no signature.");
268 return 0;
269 }
270
271 SecAsn1CoderRef decoder;
272 OSStatus status = SecAsn1CoderCreate(&decoder);
273 if (status == errSecSuccess) {
274 len = CFDataGetLength(signature);
275 bytes = CFDataGetBytePtr(signature);
276
277 status = SecAsn1Decode(decoder, bytes, len, sig_template, &raw_signature);
278 if (status == errSecSuccess) {
279 if (raw_signature.r.Length + raw_signature.s.Length > ECDSA_SHA256_SIG_SIZE) {
280 ERROR("Unexpected length %zd + %zd is not %d", raw_signature.r.Length,
281 raw_signature.s.Length, ECDSA_SHA256_SIG_SIZE);
282 } else {
283 unsigned long diff = ECDSA_SHA256_SIG_PART_SIZE - raw_signature.r.Length;
284 if (diff > 0) {
285 memset(output, 0, diff);
286 }
287 memcpy(output + diff, raw_signature.r.Data, raw_signature.r.Length);
288 diff = ECDSA_SHA256_SIG_PART_SIZE - raw_signature.s.Length;
289 if (diff > 0) {
290 memset(output + ECDSA_SHA256_SIG_PART_SIZE, 0, diff);
291 }
292 memcpy(output + ECDSA_SHA256_SIG_PART_SIZE + diff, raw_signature.s.Data, raw_signature.s.Length);
293 ret = 1;
294 }
295 }
296 SecAsn1CoderRelease(decoder);
297 }
298 if (status != errSecSuccess) {
299 srp_sec_error_print("srp_sign", status);
300 }
301 CFRelease(signature);
302 return ret;
303 }
304
305 // Local Variables:
306 // mode: C
307 // tab-width: 4
308 // c-file-style: "bsd"
309 // c-basic-offset: 4
310 // fill-column: 108
311 // indent-tabs-mode: nil
312 // End: