diff options
Diffstat (limited to 'src/lockdown-cu.c')
-rw-r--r-- | src/lockdown-cu.c | 1193 |
1 files changed, 1193 insertions, 0 deletions
diff --git a/src/lockdown-cu.c b/src/lockdown-cu.c new file mode 100644 index 0000000..1afc2c5 --- /dev/null +++ b/src/lockdown-cu.c | |||
@@ -0,0 +1,1193 @@ | |||
1 | /* | ||
2 | * lockdown-cu.c | ||
3 | * com.apple.mobile.lockdownd service CU additions | ||
4 | * | ||
5 | * Copyright (c) 2021 Nikias Bassen, All Rights Reserved. | ||
6 | * | ||
7 | * This library is free software; you can redistribute it and/or | ||
8 | * modify it under the terms of the GNU Lesser General Public | ||
9 | * License as published by the Free Software Foundation; either | ||
10 | * version 2.1 of the License, or (at your option) any later version. | ||
11 | * | ||
12 | * This library is distributed in the hope that it will be useful, | ||
13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
15 | * Lesser General Public License for more details. | ||
16 | * | ||
17 | * You should have received a copy of the GNU Lesser General Public | ||
18 | * License along with this library; if not, write to the Free Software | ||
19 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA | ||
20 | */ | ||
21 | |||
22 | #ifdef HAVE_CONFIG_H | ||
23 | #include <config.h> | ||
24 | #endif | ||
25 | |||
26 | #include <string.h> | ||
27 | #include <stdlib.h> | ||
28 | #define _GNU_SOURCE 1 | ||
29 | #define __USE_GNU 1 | ||
30 | #include <stdio.h> | ||
31 | #include <ctype.h> | ||
32 | #include <unistd.h> | ||
33 | #include <plist/plist.h> | ||
34 | |||
35 | #include "idevice.h" | ||
36 | #include "lockdown.h" | ||
37 | #include "common/debug.h" | ||
38 | |||
39 | #ifdef HAVE_WIRELESS_PAIRING | ||
40 | |||
41 | #include <libimobiledevice-glue/utils.h> | ||
42 | #include <libimobiledevice-glue/socket.h> | ||
43 | #include <libimobiledevice-glue/opack.h> | ||
44 | #include <libimobiledevice-glue/tlv.h> | ||
45 | |||
46 | #if defined(HAVE_OPENSSL) | ||
47 | #include <openssl/hmac.h> | ||
48 | #include <openssl/evp.h> | ||
49 | #include <openssl/rand.h> | ||
50 | #if defined(LIBRESSL_VERSION_NUMBER) && (LIBRESSL_VERSION_NUMBER < 0x2030200fL) | ||
51 | #include <openssl/chacha.h> | ||
52 | #include <openssl/poly1305.h> | ||
53 | #endif | ||
54 | #elif defined(HAVE_GCRYPT) | ||
55 | #include <gcrypt.h> | ||
56 | #elif defined(HAVE_MBEDTLS) | ||
57 | #include <mbedtls/md.h> | ||
58 | #include <mbedtls/chachapoly.h> | ||
59 | #endif | ||
60 | |||
61 | #ifdef __APPLE__ | ||
62 | #include <sys/sysctl.h> | ||
63 | #include <SystemConfiguration/SystemConfiguration.h> | ||
64 | #include <CoreFoundation/CoreFoundation.h> | ||
65 | #include <TargetConditionals.h> | ||
66 | #endif | ||
67 | |||
68 | #include "property_list_service.h" | ||
69 | #include "common/userpref.h" | ||
70 | |||
71 | #include "endianness.h" | ||
72 | |||
73 | #include "srp.h" | ||
74 | #include "ed25519.h" | ||
75 | |||
76 | /* {{{ SRP6a parameters */ | ||
77 | static const unsigned char kSRPModulus3072[384] = { | ||
78 | 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc9, 0x0f, 0xda, 0xa2, 0x21, 0x68, 0xc2, 0x34, | ||
79 | 0xc4, 0xc6, 0x62, 0x8b, 0x80, 0xdc, 0x1c, 0xd1, 0x29, 0x02, 0x4e, 0x08, 0x8a, 0x67, 0xcc, 0x74, | ||
80 | 0x02, 0x0b, 0xbe, 0xa6, 0x3b, 0x13, 0x9b, 0x22, 0x51, 0x4a, 0x08, 0x79, 0x8e, 0x34, 0x04, 0xdd, | ||
81 | 0xef, 0x95, 0x19, 0xb3, 0xcd, 0x3a, 0x43, 0x1b, 0x30, 0x2b, 0x0a, 0x6d, 0xf2, 0x5f, 0x14, 0x37, | ||
82 | 0x4f, 0xe1, 0x35, 0x6d, 0x6d, 0x51, 0xc2, 0x45, 0xe4, 0x85, 0xb5, 0x76, 0x62, 0x5e, 0x7e, 0xc6, | ||
83 | 0xf4, 0x4c, 0x42, 0xe9, 0xa6, 0x37, 0xed, 0x6b, 0x0b, 0xff, 0x5c, 0xb6, 0xf4, 0x06, 0xb7, 0xed, | ||
84 | 0xee, 0x38, 0x6b, 0xfb, 0x5a, 0x89, 0x9f, 0xa5, 0xae, 0x9f, 0x24, 0x11, 0x7c, 0x4b, 0x1f, 0xe6, | ||
85 | 0x49, 0x28, 0x66, 0x51, 0xec, 0xe4, 0x5b, 0x3d, 0xc2, 0x00, 0x7c, 0xb8, 0xa1, 0x63, 0xbf, 0x05, | ||
86 | 0x98, 0xda, 0x48, 0x36, 0x1c, 0x55, 0xd3, 0x9a, 0x69, 0x16, 0x3f, 0xa8, 0xfd, 0x24, 0xcf, 0x5f, | ||
87 | 0x83, 0x65, 0x5d, 0x23, 0xdc, 0xa3, 0xad, 0x96, 0x1c, 0x62, 0xf3, 0x56, 0x20, 0x85, 0x52, 0xbb, | ||
88 | 0x9e, 0xd5, 0x29, 0x07, 0x70, 0x96, 0x96, 0x6d, 0x67, 0x0c, 0x35, 0x4e, 0x4a, 0xbc, 0x98, 0x04, | ||
89 | 0xf1, 0x74, 0x6c, 0x08, 0xca, 0x18, 0x21, 0x7c, 0x32, 0x90, 0x5e, 0x46, 0x2e, 0x36, 0xce, 0x3b, | ||
90 | 0xe3, 0x9e, 0x77, 0x2c, 0x18, 0x0e, 0x86, 0x03, 0x9b, 0x27, 0x83, 0xa2, 0xec, 0x07, 0xa2, 0x8f, | ||
91 | 0xb5, 0xc5, 0x5d, 0xf0, 0x6f, 0x4c, 0x52, 0xc9, 0xde, 0x2b, 0xcb, 0xf6, 0x95, 0x58, 0x17, 0x18, | ||
92 | 0x39, 0x95, 0x49, 0x7c, 0xea, 0x95, 0x6a, 0xe5, 0x15, 0xd2, 0x26, 0x18, 0x98, 0xfa, 0x05, 0x10, | ||
93 | 0x15, 0x72, 0x8e, 0x5a, 0x8a, 0xaa, 0xc4, 0x2d, 0xad, 0x33, 0x17, 0x0d, 0x04, 0x50, 0x7a, 0x33, | ||
94 | 0xa8, 0x55, 0x21, 0xab, 0xdf, 0x1c, 0xba, 0x64, 0xec, 0xfb, 0x85, 0x04, 0x58, 0xdb, 0xef, 0x0a, | ||
95 | 0x8a, 0xea, 0x71, 0x57, 0x5d, 0x06, 0x0c, 0x7d, 0xb3, 0x97, 0x0f, 0x85, 0xa6, 0xe1, 0xe4, 0xc7, | ||
96 | 0xab, 0xf5, 0xae, 0x8c, 0xdb, 0x09, 0x33, 0xd7, 0x1e, 0x8c, 0x94, 0xe0, 0x4a, 0x25, 0x61, 0x9d, | ||
97 | 0xce, 0xe3, 0xd2, 0x26, 0x1a, 0xd2, 0xee, 0x6b, 0xf1, 0x2f, 0xfa, 0x06, 0xd9, 0x8a, 0x08, 0x64, | ||
98 | 0xd8, 0x76, 0x02, 0x73, 0x3e, 0xc8, 0x6a, 0x64, 0x52, 0x1f, 0x2b, 0x18, 0x17, 0x7b, 0x20, 0x0c, | ||
99 | 0xbb, 0xe1, 0x17, 0x57, 0x7a, 0x61, 0x5d, 0x6c, 0x77, 0x09, 0x88, 0xc0, 0xba, 0xd9, 0x46, 0xe2, | ||
100 | 0x08, 0xe2, 0x4f, 0xa0, 0x74, 0xe5, 0xab, 0x31, 0x43, 0xdb, 0x5b, 0xfc, 0xe0, 0xfd, 0x10, 0x8e, | ||
101 | 0x4b, 0x82, 0xd1, 0x20, 0xa9, 0x3a, 0xd2, 0xca, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, | ||
102 | }; | ||
103 | |||
104 | static const unsigned char kSRPGenerator5 = 5; | ||
105 | /* }}} */ | ||
106 | |||
107 | /* {{{ HKDF */ | ||
108 | #if defined(HAVE_OPENSSL) | ||
109 | #define MD_ALGO_SHA512 EVP_sha512() | ||
110 | typedef const EVP_MD* MD_ALGO_TYPE_T; | ||
111 | #define MD_ALGO_DIGEST_SIZE EVP_MD_size | ||
112 | #define MD_MAX_DIGEST_SIZE EVP_MAX_MD_SIZE | ||
113 | |||
114 | #elif defined(HAVE_GCRYPT) | ||
115 | #define MD_ALGO_SHA512 GCRY_MD_SHA512 | ||
116 | typedef int MD_ALGO_TYPE_T; | ||
117 | #define MD_ALGO_DIGEST_SIZE gcry_md_get_algo_dlen | ||
118 | #define MD_MAX_DIGEST_SIZE 64 | ||
119 | |||
120 | static void HMAC(MD_ALGO_TYPE_T md, unsigned char* key, unsigned int key_len, unsigned char* data, unsigned int data_len, unsigned char* out, unsigned int* out_len) | ||
121 | { | ||
122 | gcry_md_hd_t hd; | ||
123 | if (gcry_md_open(&hd, md, GCRY_MD_FLAG_HMAC)) { | ||
124 | debug_info("gcry_md_open() failed"); | ||
125 | return; | ||
126 | } | ||
127 | if (gcry_md_setkey(hd, key, key_len)) { | ||
128 | gcry_md_close (hd); | ||
129 | debug_info("gcry_md_setkey() failed"); | ||
130 | return; | ||
131 | } | ||
132 | gcry_md_write(hd, data, data_len); | ||
133 | |||
134 | unsigned char* digest = gcry_md_read(hd, md); | ||
135 | if (!digest) { | ||
136 | gcry_md_close(hd); | ||
137 | debug_info("gcry_md_read() failed"); | ||
138 | return; | ||
139 | } | ||
140 | |||
141 | *out_len = gcry_md_get_algo_dlen(md); | ||
142 | memcpy(out, digest, *out_len); | ||
143 | gcry_md_close(hd); | ||
144 | } | ||
145 | #elif defined(HAVE_MBEDTLS) | ||
146 | #define MD_ALGO_SHA512 MBEDTLS_MD_SHA512 | ||
147 | typedef mbedtls_md_type_t MD_ALGO_TYPE_T; | ||
148 | #define MD_ALGO_DIGEST_SIZE(x) mbedtls_md_get_size(mbedtls_md_info_from_type(x)) | ||
149 | #define MD_MAX_DIGEST_SIZE MBEDTLS_MD_MAX_SIZE | ||
150 | |||
151 | static void HMAC(MD_ALGO_TYPE_T md, unsigned char* key, unsigned int key_len, unsigned char* data, unsigned int data_len, unsigned char* out, unsigned int* out_len) | ||
152 | { | ||
153 | mbedtls_md_context_t mdctx; | ||
154 | mbedtls_md_init(&mdctx); | ||
155 | int mr = mbedtls_md_setup(&mdctx, mbedtls_md_info_from_type(md), 1); | ||
156 | if (mr != 0) { | ||
157 | debug_info("mbedtls_md_setup() failed: %d", mr); | ||
158 | return; | ||
159 | } | ||
160 | |||
161 | mr = mbedtls_md_hmac_starts(&mdctx, key, key_len); | ||
162 | if (mr != 0) { | ||
163 | mbedtls_md_free(&mdctx); | ||
164 | debug_info("mbedtls_md_hmac_starts() failed: %d", mr); | ||
165 | return; | ||
166 | } | ||
167 | |||
168 | mbedtls_md_hmac_update(&mdctx, data, data_len); | ||
169 | |||
170 | mr = mbedtls_md_hmac_finish(&mdctx, out); | ||
171 | if (mr == 0) { | ||
172 | *out_len = mbedtls_md_get_size(mbedtls_md_info_from_type(md)); | ||
173 | } else { | ||
174 | debug_info("mbedtls_md_hmac_finish() failed: %d", mr); | ||
175 | } | ||
176 | mbedtls_md_free(&mdctx); | ||
177 | } | ||
178 | #endif | ||
179 | |||
180 | static void hkdf_md_extract(MD_ALGO_TYPE_T md, unsigned char* salt, unsigned int salt_len, unsigned char* input_key_material, unsigned int input_key_material_len, unsigned char* out, unsigned int* out_len) | ||
181 | { | ||
182 | unsigned char empty_salt[MD_MAX_DIGEST_SIZE]; | ||
183 | if (!md || !out || !out_len || !*out_len) return; | ||
184 | if (salt_len == 0) { | ||
185 | salt_len = MD_ALGO_DIGEST_SIZE(md); | ||
186 | salt = (unsigned char*)empty_salt; | ||
187 | } | ||
188 | HMAC(md, salt, salt_len, input_key_material, input_key_material_len, out, out_len); | ||
189 | } | ||
190 | |||
191 | static void hkdf_md_expand(MD_ALGO_TYPE_T md, unsigned char* prk, unsigned int prk_len, unsigned char* info, unsigned int info_len, unsigned char* out, unsigned int* out_len) | ||
192 | { | ||
193 | if (!md || !out || !out_len || !*out_len) return; | ||
194 | unsigned int md_size = MD_ALGO_DIGEST_SIZE(md); | ||
195 | if (*out_len > 255 * md_size) { | ||
196 | *out_len = 0; | ||
197 | return; | ||
198 | } | ||
199 | int blocks_needed = (*out_len) / md_size; | ||
200 | if (((*out_len) % md_size) != 0) blocks_needed++; | ||
201 | unsigned int okm_len = 0; | ||
202 | unsigned char okm_block[MD_MAX_DIGEST_SIZE]; | ||
203 | unsigned int okm_block_len = 0; | ||
204 | int i; | ||
205 | for (i = 0; i < blocks_needed; i++) { | ||
206 | unsigned int output_block_len = okm_block_len + info_len + 1; | ||
207 | unsigned char* output_block = malloc(output_block_len); | ||
208 | if (okm_block_len > 0) { | ||
209 | memcpy(output_block, okm_block, okm_block_len); | ||
210 | } | ||
211 | memcpy(output_block + okm_block_len, info, info_len); | ||
212 | output_block[okm_block_len + info_len] = (uint8_t)(i+1); | ||
213 | |||
214 | HMAC(md, prk, prk_len, output_block, output_block_len, okm_block, &okm_block_len); | ||
215 | if (okm_len < *out_len) { | ||
216 | memcpy(out + okm_len, okm_block, (okm_len + okm_block_len > *out_len) ? *out_len - okm_len : okm_block_len); | ||
217 | } | ||
218 | okm_len += okm_block_len; | ||
219 | free(output_block); | ||
220 | } | ||
221 | } | ||
222 | |||
223 | static void hkdf_md(MD_ALGO_TYPE_T md, unsigned char* salt, unsigned int salt_len, unsigned char* info, unsigned int info_len, unsigned char* initial_key_material, unsigned int initial_key_material_size, unsigned char* out, unsigned int *out_len) | ||
224 | { | ||
225 | if (!md || !initial_key_material || !out || !out_len || !*out_len) return; | ||
226 | |||
227 | unsigned char prk[MD_MAX_DIGEST_SIZE]; | ||
228 | unsigned int prk_len = MD_ALGO_DIGEST_SIZE(md); | ||
229 | |||
230 | hkdf_md_extract(md, salt, salt_len, initial_key_material, initial_key_material_size, prk, &prk_len); | ||
231 | if (prk_len > 0) { | ||
232 | hkdf_md_expand(md, prk, prk_len, info, info_len, out, out_len); | ||
233 | } else { | ||
234 | *out_len = 0; | ||
235 | } | ||
236 | } | ||
237 | /* }}} */ | ||
238 | |||
239 | /* {{{ chacha20 poly1305 encryption/decryption */ | ||
240 | #if defined(HAVE_OPENSSL) && defined(LIBRESSL_VERSION_NUMBER) && (LIBRESSL_VERSION_NUMBER < 0x2030200fL) | ||
241 | /* {{{ From: OpenBSD's e_chacha20poly1305.c */ | ||
242 | /* | ||
243 | * Copyright (c) 2015 Reyk Floter <reyk@openbsd.org> | ||
244 | * Copyright (c) 2014, Google Inc. | ||
245 | * | ||
246 | * Permission to use, copy, modify, and/or distribute this software for any | ||
247 | * purpose with or without fee is hereby granted, provided that the above | ||
248 | * copyright notice and this permission notice appear in all copies. | ||
249 | * | ||
250 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | ||
251 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | ||
252 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY | ||
253 | * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | ||
254 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION | ||
255 | * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN | ||
256 | * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | ||
257 | */ | ||
258 | static void | ||
259 | poly1305_update_with_length(poly1305_state *poly1305, | ||
260 | const unsigned char *data, size_t data_len) | ||
261 | { | ||
262 | size_t j = data_len; | ||
263 | unsigned char length_bytes[8]; | ||
264 | unsigned i; | ||
265 | |||
266 | for (i = 0; i < sizeof(length_bytes); i++) { | ||
267 | length_bytes[i] = j; | ||
268 | j >>= 8; | ||
269 | } | ||
270 | |||
271 | if (data != NULL) | ||
272 | CRYPTO_poly1305_update(poly1305, data, data_len); | ||
273 | CRYPTO_poly1305_update(poly1305, length_bytes, sizeof(length_bytes)); | ||
274 | } | ||
275 | |||
276 | static void | ||
277 | poly1305_update_with_pad16(poly1305_state *poly1305, | ||
278 | const unsigned char *data, size_t data_len) | ||
279 | { | ||
280 | static const unsigned char zero_pad16[16]; | ||
281 | size_t pad_len; | ||
282 | |||
283 | CRYPTO_poly1305_update(poly1305, data, data_len); | ||
284 | |||
285 | /* pad16() is defined in RFC 7539 2.8.1. */ | ||
286 | if ((pad_len = data_len % 16) == 0) | ||
287 | return; | ||
288 | |||
289 | CRYPTO_poly1305_update(poly1305, zero_pad16, 16 - pad_len); | ||
290 | } | ||
291 | /* }}} */ | ||
292 | #endif | ||
293 | |||
294 | static void chacha20_poly1305_encrypt_96(unsigned char* key, unsigned char* nonce, unsigned char* ad, size_t ad_len, unsigned char* in, size_t in_len, unsigned char* out, size_t* out_len) | ||
295 | { | ||
296 | #if defined(HAVE_OPENSSL) | ||
297 | #if defined(LIBRESSL_VERSION_NUMBER) && (LIBRESSL_VERSION_NUMBER < 0x3050000fL) | ||
298 | #if (LIBRESSL_VERSION_NUMBER >= 0x2040000fL) | ||
299 | const EVP_AEAD *aead = EVP_aead_chacha20_poly1305(); | ||
300 | EVP_AEAD_CTX ctx; | ||
301 | EVP_AEAD_CTX_init(&ctx, aead, key, EVP_AEAD_key_length(aead), EVP_AEAD_DEFAULT_TAG_LENGTH, NULL); | ||
302 | EVP_AEAD_CTX_seal(&ctx, out, out_len, *out_len, nonce, 12, in, in_len, ad, ad_len); | ||
303 | #else | ||
304 | unsigned char poly1305_key[32]; | ||
305 | poly1305_state poly1305; | ||
306 | uint64_t ctr = (uint64_t)(nonce[0] | nonce[1] << 8 | nonce[2] << 16 | nonce[3] << 24) << 32; | ||
307 | const unsigned char* iv = nonce + 4; | ||
308 | |||
309 | memset(poly1305_key, 0, sizeof(poly1305_key)); | ||
310 | CRYPTO_chacha_20(poly1305_key, poly1305_key, sizeof(poly1305_key), key, iv, ctr); | ||
311 | |||
312 | CRYPTO_poly1305_init(&poly1305, poly1305_key); | ||
313 | poly1305_update_with_pad16(&poly1305, ad, ad_len); | ||
314 | CRYPTO_chacha_20(out, in, in_len, key, iv, ctr + 1); | ||
315 | poly1305_update_with_pad16(&poly1305, out, in_len); | ||
316 | poly1305_update_with_length(&poly1305, NULL, ad_len); | ||
317 | poly1305_update_with_length(&poly1305, NULL, in_len); | ||
318 | |||
319 | CRYPTO_poly1305_finish(&poly1305, out + in_len); | ||
320 | |||
321 | *out_len = in_len + 16; | ||
322 | #endif | ||
323 | #elif defined(OPENSSL_VERSION_NUMBER) && (OPENSSL_VERSION_NUMBER >= 0x10100000L) | ||
324 | int outl = 0; | ||
325 | EVP_CIPHER_CTX* ctx = EVP_CIPHER_CTX_new(); | ||
326 | EVP_EncryptInit_ex(ctx, EVP_chacha20_poly1305(), NULL, key, nonce); | ||
327 | EVP_EncryptUpdate(ctx, out, &outl, in, in_len); | ||
328 | *out_len = outl; | ||
329 | outl = 0; | ||
330 | EVP_EncryptFinal_ex(ctx, out + *out_len, &outl); | ||
331 | *out_len += outl; | ||
332 | EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_AEAD_GET_TAG, 16, out + *out_len); | ||
333 | EVP_CIPHER_CTX_free(ctx); | ||
334 | *out_len += 16; | ||
335 | #else | ||
336 | #error Please use a newer version of OpenSSL (>= 1.1.0) | ||
337 | #endif | ||
338 | #elif defined(HAVE_GCRYPT) | ||
339 | #if defined(GCRYPT_VERSION_NUMBER) && (GCRYPT_VERSION_NUMBER >= 0x010700) | ||
340 | gcry_cipher_hd_t hd; | ||
341 | if (gcry_cipher_open(&hd, GCRY_CIPHER_CHACHA20, GCRY_CIPHER_MODE_POLY1305, 0)) { | ||
342 | debug_info("gcry_cipher_open() failed"); | ||
343 | return; | ||
344 | } | ||
345 | gcry_cipher_setkey(hd, key, 32); | ||
346 | gcry_cipher_setiv(hd, nonce, 12); | ||
347 | gcry_cipher_authenticate(hd, ad, ad_len); | ||
348 | *out_len = in_len + 16; | ||
349 | if (gcry_cipher_encrypt(hd, out, *out_len, in, in_len)) { | ||
350 | *out_len = 0; | ||
351 | } | ||
352 | gcry_cipher_gettag(hd, out+in_len, 16); | ||
353 | gcry_cipher_close(hd); | ||
354 | #else | ||
355 | #error Please use a newer version of libgcrypt (>= 1.7.0) | ||
356 | #endif | ||
357 | #elif defined (HAVE_MBEDTLS) | ||
358 | mbedtls_chachapoly_context ctx; | ||
359 | mbedtls_chachapoly_init(&ctx); | ||
360 | mbedtls_chachapoly_setkey(&ctx, key); | ||
361 | if (mbedtls_chachapoly_encrypt_and_tag(&ctx, in_len, nonce, ad, ad_len, in, out, out+in_len) != 0) { | ||
362 | *out_len = 0; | ||
363 | } | ||
364 | mbedtls_chachapoly_free(&ctx); | ||
365 | #else | ||
366 | #error chacha20_poly1305_encrypt_96 is not implemented | ||
367 | #endif | ||
368 | } | ||
369 | |||
370 | static void chacha20_poly1305_encrypt_64(unsigned char* key, unsigned char* nonce, unsigned char* ad, size_t ad_len, unsigned char* in, size_t in_len, unsigned char* out, size_t* out_len) | ||
371 | { | ||
372 | unsigned char _nonce[12]; | ||
373 | *(uint32_t*)(&_nonce[0]) = 0; | ||
374 | memcpy(&_nonce[4], nonce, 8); | ||
375 | chacha20_poly1305_encrypt_96(key, _nonce, ad, ad_len, in, in_len, out, out_len); | ||
376 | } | ||
377 | |||
378 | static void chacha20_poly1305_decrypt_96(unsigned char* key, unsigned char* nonce, unsigned char* ad, size_t ad_len, unsigned char* in, size_t in_len, unsigned char* out, size_t* out_len) | ||
379 | { | ||
380 | #if defined(HAVE_OPENSSL) | ||
381 | #if defined(LIBRESSL_VERSION_NUMBER) && (LIBRESSL_VERSION_NUMBER < 0x3050000fL) | ||
382 | #if (LIBRESSL_VERSION_NUMBER >= 0x2040000fL) | ||
383 | const EVP_AEAD *aead = EVP_aead_chacha20_poly1305(); | ||
384 | EVP_AEAD_CTX ctx; | ||
385 | EVP_AEAD_CTX_init(&ctx, aead, key, EVP_AEAD_key_length(aead), EVP_AEAD_DEFAULT_TAG_LENGTH, NULL); | ||
386 | EVP_AEAD_CTX_open(&ctx, out, out_len, *out_len, nonce, 12, in, in_len, ad, ad_len); | ||
387 | #else | ||
388 | unsigned char mac[16]; | ||
389 | unsigned char poly1305_key[32]; | ||
390 | poly1305_state poly1305; | ||
391 | size_t plaintext_len = in_len - 16; | ||
392 | uint64_t ctr = (uint64_t)(nonce[0] | nonce[1] << 8 | nonce[2] << 16 | nonce[3] << 24) << 32; | ||
393 | const unsigned char *iv = nonce + 4; | ||
394 | |||
395 | memset(poly1305_key, 0, sizeof(poly1305_key)); | ||
396 | CRYPTO_chacha_20(poly1305_key, poly1305_key, sizeof(poly1305_key), key, iv, ctr); | ||
397 | |||
398 | CRYPTO_poly1305_init(&poly1305, poly1305_key); | ||
399 | poly1305_update_with_pad16(&poly1305, ad, ad_len); | ||
400 | poly1305_update_with_pad16(&poly1305, in, plaintext_len); | ||
401 | poly1305_update_with_length(&poly1305, NULL, ad_len); | ||
402 | poly1305_update_with_length(&poly1305, NULL, plaintext_len); | ||
403 | |||
404 | CRYPTO_poly1305_finish(&poly1305, mac); | ||
405 | |||
406 | if (memcmp(mac, in + plaintext_len, 16) != 0) { | ||
407 | *out_len = 0; | ||
408 | return; | ||
409 | } | ||
410 | |||
411 | CRYPTO_chacha_20(out, in, plaintext_len, key, iv, ctr + 1); | ||
412 | *out_len = plaintext_len; | ||
413 | #endif | ||
414 | #elif defined(OPENSSL_VERSION_NUMBER) && (OPENSSL_VERSION_NUMBER >= 0x10100000L) | ||
415 | int outl = 0; | ||
416 | size_t plaintext_len = in_len - 16; | ||
417 | EVP_CIPHER_CTX* ctx = EVP_CIPHER_CTX_new(); | ||
418 | EVP_DecryptInit_ex(ctx, EVP_chacha20_poly1305(), NULL, key, nonce); | ||
419 | EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_AEAD_SET_TAG, 16, in + plaintext_len); | ||
420 | EVP_DecryptUpdate(ctx, out, &outl, in, plaintext_len); | ||
421 | *out_len = outl; | ||
422 | outl = 0; | ||
423 | if (EVP_DecryptFinal_ex(ctx, out + *out_len, &outl) == 1) { | ||
424 | *out_len += outl; | ||
425 | } else { | ||
426 | *out_len = 0; | ||
427 | } | ||
428 | EVP_CIPHER_CTX_free(ctx); | ||
429 | #else | ||
430 | #error Please use a newer version of OpenSSL (>= 1.1.0) | ||
431 | #endif | ||
432 | #elif defined(HAVE_GCRYPT) | ||
433 | #if defined(GCRYPT_VERSION_NUMBER) && (GCRYPT_VERSION_NUMBER >= 0x010700) | ||
434 | gcry_cipher_hd_t hd; | ||
435 | if (gcry_cipher_open(&hd, GCRY_CIPHER_CHACHA20, GCRY_CIPHER_MODE_POLY1305, 0)) { | ||
436 | debug_info("gcry_cipher_open() failed"); | ||
437 | return; | ||
438 | } | ||
439 | gcry_cipher_setkey(hd, key, 32); | ||
440 | gcry_cipher_setiv(hd, nonce, 12); | ||
441 | gcry_cipher_authenticate(hd, ad, ad_len); | ||
442 | unsigned int plaintext_len = in_len - 16; | ||
443 | gcry_cipher_decrypt(hd, out, *out_len, in, plaintext_len); | ||
444 | if (gcry_cipher_checktag(hd, in + plaintext_len, 16) == 0) { | ||
445 | *out_len = plaintext_len; | ||
446 | } else { | ||
447 | *out_len = 0; | ||
448 | } | ||
449 | gcry_cipher_close(hd); | ||
450 | #else | ||
451 | #error Please use a newer version of libgcrypt (>= 1.7.0) | ||
452 | #endif | ||
453 | #elif defined(HAVE_MBEDTLS) | ||
454 | mbedtls_chachapoly_context ctx; | ||
455 | mbedtls_chachapoly_init(&ctx); | ||
456 | mbedtls_chachapoly_setkey(&ctx, key); | ||
457 | unsigned int plaintext_len = in_len - 16; | ||
458 | if (mbedtls_chachapoly_auth_decrypt(&ctx, plaintext_len, nonce, ad, ad_len, in + plaintext_len, in, out) == 0) { | ||
459 | *out_len = plaintext_len; | ||
460 | } else { | ||
461 | *out_len = 0; | ||
462 | } | ||
463 | mbedtls_chachapoly_free(&ctx); | ||
464 | #else | ||
465 | #error chacha20_poly1305_decrypt_96 is not implemented | ||
466 | #endif | ||
467 | } | ||
468 | |||
469 | static void chacha20_poly1305_decrypt_64(unsigned char* key, unsigned char* nonce, unsigned char* ad, size_t ad_len, unsigned char* in, size_t in_len, unsigned char* out, size_t* out_len) | ||
470 | { | ||
471 | unsigned char _nonce[12]; | ||
472 | *(uint32_t*)(&_nonce[0]) = 0; | ||
473 | memcpy(&_nonce[4], nonce, 8); | ||
474 | chacha20_poly1305_decrypt_96(key, _nonce, ad, ad_len, in, in_len, out, out_len); | ||
475 | } | ||
476 | /* }}} */ | ||
477 | |||
478 | #define PAIRING_ERROR(x) \ | ||
479 | debug_info(x); \ | ||
480 | if (pairing_callback) { \ | ||
481 | pairing_callback(LOCKDOWN_CU_PAIRING_ERROR, cb_user_data, (char*)x, NULL); \ | ||
482 | } | ||
483 | |||
484 | #define PAIRING_ERROR_FMT(...) \ | ||
485 | sprintf(tmp, __VA_ARGS__); \ | ||
486 | debug_info(tmp); \ | ||
487 | if (pairing_callback) { \ | ||
488 | pairing_callback(LOCKDOWN_CU_PAIRING_ERROR, cb_user_data, tmp, NULL); \ | ||
489 | } | ||
490 | |||
491 | #endif /* HAVE_WIRELESS_PAIRING */ | ||
492 | |||
493 | lockdownd_error_t lockdownd_cu_pairing_create(lockdownd_client_t client, lockdownd_cu_pairing_cb_t pairing_callback, void* cb_user_data, plist_t host_info, plist_t acl) | ||
494 | { | ||
495 | #ifdef HAVE_WIRELESS_PAIRING | ||
496 | if (!client || !pairing_callback || (host_info && plist_get_node_type(host_info) != PLIST_DICT) || (acl && plist_get_node_type(acl) != PLIST_DICT)) | ||
497 | return LOCKDOWN_E_INVALID_ARG; | ||
498 | |||
499 | lockdownd_error_t ret = LOCKDOWN_E_UNKNOWN_ERROR; | ||
500 | |||
501 | if (client->device && client->device->version == 0) { | ||
502 | plist_t p_version = NULL; | ||
503 | if (lockdownd_get_value(client, NULL, "ProductVersion", &p_version) == LOCKDOWN_E_SUCCESS) { | ||
504 | int vers[3] = {0, 0, 0}; | ||
505 | char *s_version = NULL; | ||
506 | plist_get_string_val(p_version, &s_version); | ||
507 | if (s_version && sscanf(s_version, "%d.%d.%d", &vers[0], &vers[1], &vers[2]) >= 2) { | ||
508 | client->device->version = DEVICE_VERSION(vers[0], vers[1], vers[2]); | ||
509 | } | ||
510 | free(s_version); | ||
511 | } | ||
512 | plist_free(p_version); | ||
513 | } | ||
514 | |||
515 | char* pairing_uuid = NULL; | ||
516 | if (host_info) { | ||
517 | plist_t accountid = plist_dict_get_item(host_info, "accountID"); | ||
518 | if (accountid && plist_get_node_type(accountid) == PLIST_STRING) { | ||
519 | plist_get_string_val(accountid, &pairing_uuid); | ||
520 | } | ||
521 | } | ||
522 | if (!pairing_uuid) { | ||
523 | userpref_read_system_buid(&pairing_uuid); | ||
524 | } | ||
525 | if (!pairing_uuid) { | ||
526 | pairing_uuid = generate_uuid(); | ||
527 | } | ||
528 | unsigned int pairing_uuid_len = strlen(pairing_uuid); | ||
529 | |||
530 | SRP_initialize_library(); | ||
531 | |||
532 | SRP* srp = SRP_new(SRP6a_sha512_client_method()); | ||
533 | if (!srp) { | ||
534 | PAIRING_ERROR("Failed to initialize SRP") | ||
535 | return LOCKDOWN_E_UNKNOWN_ERROR; | ||
536 | } | ||
537 | |||
538 | char tmp[256]; | ||
539 | plist_t dict = NULL; | ||
540 | uint8_t current_state = 0; | ||
541 | uint8_t final_state = 6; | ||
542 | |||
543 | unsigned char* salt = NULL; | ||
544 | unsigned int salt_size = 0; | ||
545 | unsigned char* pubkey = NULL; | ||
546 | unsigned int pubkey_size = 0; | ||
547 | |||
548 | unsigned char setup_encryption_key[32]; | ||
549 | |||
550 | cstr *thekey = NULL; | ||
551 | |||
552 | do { | ||
553 | current_state++; | ||
554 | |||
555 | dict = plist_new_dict(); | ||
556 | plist_dict_set_item(dict, "Request", plist_new_string("CUPairingCreate")); | ||
557 | if (current_state == 1) { | ||
558 | plist_dict_set_item(dict, "Flags", plist_new_uint(1)); | ||
559 | } else { | ||
560 | plist_dict_set_item(dict, "Flags", plist_new_uint(0)); | ||
561 | } | ||
562 | |||
563 | tlv_buf_t tlv = tlv_buf_new(); | ||
564 | |||
565 | if (current_state == 1) { | ||
566 | /* send method */ | ||
567 | tlv_buf_append(tlv, 0x00, 1, (void*)"\x00"); // 0x00 (Method), 1 bytes, 00 | ||
568 | } else if (current_state == 3) { | ||
569 | /* generate public key */ | ||
570 | cstr* own_pub = NULL; | ||
571 | SRP_gen_pub(srp, &own_pub); | ||
572 | |||
573 | if (!own_pub) { | ||
574 | PAIRING_ERROR("[SRP] Failed to generate public key") | ||
575 | ret = LOCKDOWN_E_PAIRING_FAILED; | ||
576 | break; | ||
577 | } | ||
578 | |||
579 | /* compute key from remote's public key */ | ||
580 | if (SRP_compute_key(srp, &thekey, pubkey, pubkey_size) != 0) { | ||
581 | cstr_free(own_pub); | ||
582 | PAIRING_ERROR("[SRP] Failed to compute key") | ||
583 | ret = LOCKDOWN_E_PAIRING_FAILED; | ||
584 | break; | ||
585 | } | ||
586 | |||
587 | /* compute response */ | ||
588 | cstr *response = NULL; | ||
589 | SRP_respond(srp, &response); | ||
590 | |||
591 | /* send our public key + response */ | ||
592 | tlv_buf_append(tlv, 0x03, own_pub->length, own_pub->data); | ||
593 | tlv_buf_append(tlv, 0x04, response->length, response->data); | ||
594 | cstr_free(response); | ||
595 | cstr_free(own_pub); | ||
596 | } else if (current_state == 5) { | ||
597 | /* send encrypted info */ | ||
598 | |||
599 | static const char PAIR_SETUP_ENCRYPT_SALT[] = "Pair-Setup-Encrypt-Salt"; | ||
600 | static const char PAIR_SETUP_ENCRYPT_INFO[] = "Pair-Setup-Encrypt-Info"; | ||
601 | static const char PAIR_SETUP_CONTROLLER_SIGN_SALT[] = "Pair-Setup-Controller-Sign-Salt"; | ||
602 | static const char PAIR_SETUP_CONTROLLER_SIGN_INFO[] = "Pair-Setup-Controller-Sign-Info"; | ||
603 | |||
604 | // HKDF with above computed key (SRP_compute_key) + Pair-Setup-Encrypt-Salt + Pair-Setup-Encrypt-Info | ||
605 | // result used as key for chacha20-poly1305 | ||
606 | unsigned int setup_encryption_key_len = sizeof(setup_encryption_key); | ||
607 | hkdf_md(MD_ALGO_SHA512, (unsigned char*)PAIR_SETUP_ENCRYPT_SALT, sizeof(PAIR_SETUP_ENCRYPT_SALT)-1, (unsigned char*)PAIR_SETUP_ENCRYPT_INFO, sizeof(PAIR_SETUP_ENCRYPT_INFO)-1, (unsigned char*)thekey->data, thekey->length, setup_encryption_key, &setup_encryption_key_len); | ||
608 | |||
609 | unsigned char ed25519_pubkey[32]; | ||
610 | unsigned char ed25519_privkey[64]; | ||
611 | unsigned char ed25519seed[32]; | ||
612 | ed25519_create_seed(ed25519seed); | ||
613 | |||
614 | ed25519_create_keypair(ed25519_pubkey, ed25519_privkey, ed25519seed); | ||
615 | |||
616 | unsigned int signbuf_len = pairing_uuid_len + 64; | ||
617 | unsigned char* signbuf = malloc(signbuf_len); | ||
618 | unsigned int hkdf_len = 32; | ||
619 | // HKDF with above computed key (SRP_compute_key) + Pair-Setup-Controller-Sign-Salt + Pair-Setup-Controller-Sign-Info | ||
620 | hkdf_md(MD_ALGO_SHA512, (unsigned char*)PAIR_SETUP_CONTROLLER_SIGN_SALT, sizeof(PAIR_SETUP_CONTROLLER_SIGN_SALT)-1, (unsigned char*)PAIR_SETUP_CONTROLLER_SIGN_INFO, sizeof(PAIR_SETUP_CONTROLLER_SIGN_INFO)-1, (unsigned char*)thekey->data, thekey->length, signbuf, &hkdf_len); | ||
621 | |||
622 | memcpy(signbuf + 32, pairing_uuid, pairing_uuid_len); | ||
623 | memcpy(signbuf + 32 + pairing_uuid_len, ed25519_pubkey, 32); | ||
624 | |||
625 | unsigned char ed_sig[64]; | ||
626 | ed25519_sign(ed_sig, signbuf, 0x64, ed25519_pubkey, ed25519_privkey); | ||
627 | |||
628 | tlv_buf_t tlvbuf = tlv_buf_new(); | ||
629 | tlv_buf_append(tlvbuf, 0x01, pairing_uuid_len, (void*)pairing_uuid); | ||
630 | tlv_buf_append(tlvbuf, 0x03, sizeof(ed25519_pubkey), ed25519_pubkey); | ||
631 | tlv_buf_append(tlvbuf, 0x0a, sizeof(ed_sig), ed_sig); | ||
632 | |||
633 | /* ACL */ | ||
634 | unsigned char* odata = NULL; | ||
635 | unsigned int olen = 0; | ||
636 | if (acl) { | ||
637 | opack_encode_from_plist(acl, &odata, &olen); | ||
638 | } else { | ||
639 | /* defaut ACL */ | ||
640 | plist_t acl_plist = plist_new_dict(); | ||
641 | plist_dict_set_item(acl_plist, "com.apple.ScreenCapture", plist_new_bool(1)); | ||
642 | plist_dict_set_item(acl_plist, "com.apple.developer", plist_new_bool(1)); | ||
643 | opack_encode_from_plist(acl_plist, &odata, &olen); | ||
644 | plist_free(acl_plist); | ||
645 | } | ||
646 | tlv_buf_append(tlvbuf, 0x12, olen, odata); | ||
647 | free(odata); | ||
648 | |||
649 | /* HOST INFORMATION */ | ||
650 | char hostname[256]; | ||
651 | #if defined(__APPLE__) && !defined(TARGET_OS_IPHONE) | ||
652 | CFStringRef cname = SCDynamicStoreCopyComputerName(NULL, NULL); | ||
653 | CFStringGetCString(cname, hostname, sizeof(hostname), kCFStringEncodingUTF8); | ||
654 | CFRelease(cname); | ||
655 | #else | ||
656 | #ifdef WIN32 | ||
657 | DWORD hostname_len = sizeof(hostname); | ||
658 | GetComputerName(hostname, &hostname_len); | ||
659 | #else | ||
660 | gethostname(hostname, sizeof(hostname)); | ||
661 | #endif | ||
662 | #endif | ||
663 | |||
664 | char modelname[256]; | ||
665 | modelname[0] = '\0'; | ||
666 | #ifdef __APPLE__ | ||
667 | size_t len = sizeof(modelname); | ||
668 | sysctlbyname("hw.model", &modelname, &len, NULL, 0); | ||
669 | #endif | ||
670 | if (strlen(modelname) == 0) { | ||
671 | strcpy(modelname, "HackbookPro13,37"); | ||
672 | } | ||
673 | |||
674 | unsigned char primary_mac_addr[6] = { 0, 0, 0, 0, 0, 0 }; | ||
675 | if (get_primary_mac_address(primary_mac_addr) != 0) { | ||
676 | debug_info("Failed to get primary mac address"); | ||
677 | } | ||
678 | debug_info("Primary mac address: %02x:%02x:%02x:%02x:%02x:%02x\n", primary_mac_addr[0], primary_mac_addr[1], primary_mac_addr[2], primary_mac_addr[3], primary_mac_addr[4], primary_mac_addr[5]); | ||
679 | |||
680 | // "OPACK" encoded device info | ||
681 | plist_t info_plist = plist_new_dict(); | ||
682 | //plist_dict_set_item(info_plist, "altIRK", plist_new_data((char*)altIRK, 16)); | ||
683 | plist_dict_set_item(info_plist, "accountID", plist_new_string(pairing_uuid)); | ||
684 | plist_dict_set_item(info_plist, "model", plist_new_string(modelname)); | ||
685 | plist_dict_set_item(info_plist, "name", plist_new_string(hostname)); | ||
686 | plist_dict_set_item(info_plist, "mac", plist_new_data((char*)primary_mac_addr, 6)); | ||
687 | if (host_info) { | ||
688 | plist_dict_merge(&info_plist, host_info); | ||
689 | } | ||
690 | opack_encode_from_plist(info_plist, &odata, &olen); | ||
691 | plist_free(info_plist); | ||
692 | tlv_buf_append(tlvbuf, 0x11, olen, odata); | ||
693 | free(odata); | ||
694 | |||
695 | size_t encrypted_len = tlvbuf->length + 16; | ||
696 | unsigned char* encrypted_buf = (unsigned char*)malloc(encrypted_len); | ||
697 | |||
698 | chacha20_poly1305_encrypt_64(setup_encryption_key, (unsigned char*)"PS-Msg05", NULL, 0, tlvbuf->data, tlvbuf->length, encrypted_buf, &encrypted_len); | ||
699 | |||
700 | tlv_buf_free(tlvbuf); | ||
701 | |||
702 | tlv_buf_append(tlv, 0x05, encrypted_len, encrypted_buf); | ||
703 | free(encrypted_buf); | ||
704 | } else { | ||
705 | tlv_buf_free(tlv); | ||
706 | PAIRING_ERROR("[SRP] Invalid state"); | ||
707 | ret = LOCKDOWN_E_PAIRING_FAILED; | ||
708 | break; | ||
709 | } | ||
710 | tlv_buf_append(tlv, 0x06, 1, ¤t_state); | ||
711 | plist_dict_set_item(dict, "Payload", plist_new_data((char*)tlv->data, tlv->length)); | ||
712 | tlv_buf_free(tlv); | ||
713 | |||
714 | plist_dict_set_item(dict, "Label", plist_new_string(client->label)); | ||
715 | plist_dict_set_item(dict, "ProtocolVersion", plist_new_uint(2)); | ||
716 | |||
717 | ret = lockdownd_send(client, dict); | ||
718 | plist_free(dict); | ||
719 | dict = NULL; | ||
720 | |||
721 | if (ret != LOCKDOWN_E_SUCCESS) { | ||
722 | break; | ||
723 | } | ||
724 | |||
725 | current_state++; | ||
726 | |||
727 | ret = lockdownd_receive(client, &dict); | ||
728 | if (ret != LOCKDOWN_E_SUCCESS) { | ||
729 | break; | ||
730 | } | ||
731 | ret = lockdown_check_result(dict, "CUPairingCreate"); | ||
732 | if (ret != LOCKDOWN_E_SUCCESS) { | ||
733 | break; | ||
734 | } | ||
735 | |||
736 | plist_t extresp = plist_dict_get_item(dict, "ExtendedResponse"); | ||
737 | if (!extresp) { | ||
738 | ret = LOCKDOWN_E_PLIST_ERROR; | ||
739 | break; | ||
740 | } | ||
741 | plist_t blob = plist_dict_get_item(extresp, "Payload"); | ||
742 | if (!blob) { | ||
743 | ret = LOCKDOWN_E_PLIST_ERROR; | ||
744 | break; | ||
745 | } | ||
746 | uint64_t data_len = 0; | ||
747 | const char* data = plist_get_data_ptr(blob, &data_len); | ||
748 | |||
749 | uint8_t state = 0; | ||
750 | if (!tlv_data_get_uint8(data, data_len, 0x06, &state)) { | ||
751 | PAIRING_ERROR("[SRP] ERROR: Could not find state in response"); | ||
752 | ret = LOCKDOWN_E_PAIRING_FAILED; | ||
753 | break; | ||
754 | } | ||
755 | if (state != current_state) { | ||
756 | PAIRING_ERROR_FMT("[SRP] ERROR: Unexpected state %d, expected %d", state, current_state); | ||
757 | ret = LOCKDOWN_E_PAIRING_FAILED; | ||
758 | break; | ||
759 | } | ||
760 | |||
761 | unsigned int errval = 0; | ||
762 | uint64_t u64val = 0; | ||
763 | tlv_data_get_uint(data, data_len, 0x07, &u64val); | ||
764 | debug_buffer(data, data_len); | ||
765 | errval = (unsigned int)u64val; | ||
766 | if (errval > 0) { | ||
767 | if (errval == 3) { | ||
768 | u64val = 0; | ||
769 | tlv_data_get_uint(data, data_len, 0x08, &u64val); | ||
770 | if (u64val > 0) { | ||
771 | uint32_t retry_delay = (uint32_t)u64val; | ||
772 | PAIRING_ERROR_FMT("[SRP] Pairing is blocked for another %u seconds", retry_delay) | ||
773 | ret = LOCKDOWN_E_PAIRING_FAILED; | ||
774 | break; | ||
775 | } | ||
776 | } else if (errval == 2 && state == 4) { | ||
777 | PAIRING_ERROR_FMT("[SRP] Invalid PIN") | ||
778 | ret = LOCKDOWN_E_PAIRING_FAILED; | ||
779 | break; | ||
780 | } else { | ||
781 | PAIRING_ERROR_FMT("[SRP] Received error %u in state %d.", errval, state); | ||
782 | ret = LOCKDOWN_E_PAIRING_FAILED; | ||
783 | break; | ||
784 | } | ||
785 | } | ||
786 | |||
787 | if (state == 2) { | ||
788 | /* receive salt and public key */ | ||
789 | if (!tlv_data_copy_data(data, data_len, 0x02, (void**)&salt, &salt_size)) { | ||
790 | PAIRING_ERROR("[SRP] ERROR: Could not find salt in response"); | ||
791 | ret = LOCKDOWN_E_PAIRING_FAILED; | ||
792 | break; | ||
793 | } | ||
794 | if (!tlv_data_copy_data(data, data_len, 0x03, (void**)&pubkey, &pubkey_size)) { | ||
795 | PAIRING_ERROR("[SRP] ERROR: Could not find public key in response"); | ||
796 | |||
797 | ret = LOCKDOWN_E_PAIRING_FAILED; | ||
798 | break; | ||
799 | } | ||
800 | |||
801 | const char PAIR_SETUP[] = "Pair-Setup"; | ||
802 | if (SRP_set_user_raw(srp, (const unsigned char*)PAIR_SETUP, sizeof(PAIR_SETUP)-1) != 0) { | ||
803 | PAIRING_ERROR("[SRP] Failed to set SRP user"); | ||
804 | ret = LOCKDOWN_E_PAIRING_FAILED; | ||
805 | break; | ||
806 | } | ||
807 | |||
808 | /* kSRPParameters_3072_SHA512 */ | ||
809 | if (SRP_set_params(srp, kSRPModulus3072, sizeof(kSRPModulus3072), &kSRPGenerator5, 1, salt, salt_size) != 0) { | ||
810 | PAIRING_ERROR("[SRP] Failed to set SRP parameters"); | ||
811 | ret = LOCKDOWN_E_PAIRING_FAILED; | ||
812 | break; | ||
813 | |||
814 | } | ||
815 | |||
816 | if (pairing_callback) { | ||
817 | char pin[64]; | ||
818 | unsigned int pin_len = sizeof(pin); | ||
819 | pairing_callback(LOCKDOWN_CU_PAIRING_PIN_REQUESTED, cb_user_data, pin, &pin_len); | ||
820 | |||
821 | SRP_set_auth_password_raw(srp, (const unsigned char*)pin, pin_len); | ||
822 | } | ||
823 | } else if (state == 4) { | ||
824 | /* receive proof */ | ||
825 | unsigned char* proof = NULL; | ||
826 | unsigned int proof_len = 0; | ||
827 | |||
828 | if (!tlv_data_copy_data(data, data_len, 0x04, (void**)&proof, &proof_len)) { | ||
829 | PAIRING_ERROR("[SRP] ERROR: Could not find proof data in response"); | ||
830 | ret = LOCKDOWN_E_PAIRING_FAILED; | ||
831 | break; | ||
832 | } | ||
833 | |||
834 | /* verify */ | ||
835 | int vrfy_result = SRP_verify(srp, proof, proof_len); | ||
836 | free(proof); | ||
837 | |||
838 | if (vrfy_result == 0) { | ||
839 | debug_info("[SRP] PIN verified successfully"); | ||
840 | } else { | ||
841 | PAIRING_ERROR("[SRP] PIN verification failure"); | ||
842 | ret = LOCKDOWN_E_PAIRING_FAILED; | ||
843 | break; | ||
844 | } | ||
845 | |||
846 | } else if (state == 6) { | ||
847 | int srp_pair_success = 0; | ||
848 | plist_t node = plist_dict_get_item(extresp, "doSRPPair"); | ||
849 | if (node) { | ||
850 | const char* strv = plist_get_string_ptr(node, NULL); | ||
851 | if (strcmp(strv, "succeed") == 0) { | ||
852 | srp_pair_success = 1; | ||
853 | } | ||
854 | } | ||
855 | if (!srp_pair_success) { | ||
856 | PAIRING_ERROR("SRP Pairing failed"); | ||
857 | ret = LOCKDOWN_E_PAIRING_FAILED; | ||
858 | break; | ||
859 | } | ||
860 | |||
861 | /* receive encrypted info */ | ||
862 | unsigned char* encrypted_buf = NULL; | ||
863 | unsigned int enc_len = 0; | ||
864 | if (!tlv_data_copy_data(data, data_len, 0x05, (void**)&encrypted_buf, &enc_len)) { | ||
865 | PAIRING_ERROR("[SRP] ERROR: Could not find encrypted data in response"); | ||
866 | ret = LOCKDOWN_E_PAIRING_FAILED; | ||
867 | break; | ||
868 | } | ||
869 | size_t plain_len = enc_len-16; | ||
870 | unsigned char* plain_buf = malloc(plain_len); | ||
871 | chacha20_poly1305_decrypt_64(setup_encryption_key, (unsigned char*)"PS-Msg06", NULL, 0, encrypted_buf, enc_len, plain_buf, &plain_len); | ||
872 | free(encrypted_buf); | ||
873 | |||
874 | unsigned char* dev_info = NULL; | ||
875 | unsigned int dev_info_len = 0; | ||
876 | int res = tlv_data_copy_data(plain_buf, plain_len, 0x11, (void**)&dev_info, &dev_info_len); | ||
877 | free(plain_buf); | ||
878 | if (!res) { | ||
879 | PAIRING_ERROR("[SRP] ERROR: Failed to locate device info in response"); | ||
880 | ret = LOCKDOWN_E_PAIRING_FAILED; | ||
881 | break; | ||
882 | } | ||
883 | plist_t device_info = NULL; | ||
884 | opack_decode_to_plist(dev_info, dev_info_len, &device_info); | ||
885 | free(dev_info); | ||
886 | |||
887 | if (!device_info) { | ||
888 | PAIRING_ERROR("[SRP] ERROR: Failed to parse device info"); | ||
889 | ret = LOCKDOWN_E_PAIRING_FAILED; | ||
890 | break; | ||
891 | } | ||
892 | |||
893 | if (pairing_callback) { | ||
894 | pairing_callback(LOCKDOWN_CU_PAIRING_DEVICE_INFO, cb_user_data, device_info, NULL); | ||
895 | } | ||
896 | plist_free(device_info); | ||
897 | } else { | ||
898 | PAIRING_ERROR("[SRP] ERROR: Invalid state"); | ||
899 | ret = LOCKDOWN_E_PAIRING_FAILED; | ||
900 | break; | ||
901 | } | ||
902 | plist_free(dict); | ||
903 | dict = NULL; | ||
904 | |||
905 | } while (current_state != final_state); | ||
906 | |||
907 | plist_free(dict); | ||
908 | |||
909 | free(salt); | ||
910 | free(pubkey); | ||
911 | |||
912 | SRP_free(srp); | ||
913 | srp = NULL; | ||
914 | |||
915 | if (ret != LOCKDOWN_E_SUCCESS) { | ||
916 | if (thekey) { | ||
917 | cstr_free(thekey); | ||
918 | } | ||
919 | return ret; | ||
920 | } | ||
921 | |||
922 | free(client->cu_key); | ||
923 | client->cu_key = malloc(thekey->length); | ||
924 | memcpy(client->cu_key, thekey->data, thekey->length); | ||
925 | client->cu_key_len = thekey->length; | ||
926 | cstr_free(thekey); | ||
927 | |||
928 | return LOCKDOWN_E_SUCCESS; | ||
929 | #else | ||
930 | debug_info("not supported"); | ||
931 | return LOCKDOWN_E_UNKNOWN_ERROR; | ||
932 | #endif | ||
933 | } | ||
934 | |||
935 | lockdownd_error_t lockdownd_cu_send_request_and_get_reply(lockdownd_client_t client, const char* request, plist_t request_payload, plist_t* reply) | ||
936 | { | ||
937 | #ifdef HAVE_WIRELESS_PAIRING | ||
938 | if (!client || !request) | ||
939 | return LOCKDOWN_E_INVALID_ARG; | ||
940 | |||
941 | if (!client->cu_key) | ||
942 | return LOCKDOWN_E_NO_RUNNING_SESSION; | ||
943 | |||
944 | lockdownd_error_t ret = LOCKDOWN_E_UNKNOWN_ERROR; | ||
945 | |||
946 | /* derive keys */ | ||
947 | unsigned char cu_write_key[32]; | ||
948 | unsigned int cu_write_key_len = sizeof(cu_write_key); | ||
949 | static const char WRITE_KEY_SALT_MDLD[] = "WriteKeySaltMDLD"; | ||
950 | static const char WRITE_KEY_INFO_MDLD[] = "WriteKeyInfoMDLD"; | ||
951 | hkdf_md(MD_ALGO_SHA512, (unsigned char*)WRITE_KEY_SALT_MDLD, sizeof(WRITE_KEY_SALT_MDLD)-1, (unsigned char*)WRITE_KEY_INFO_MDLD, sizeof(WRITE_KEY_INFO_MDLD)-1, client->cu_key, client->cu_key_len, cu_write_key, &cu_write_key_len); | ||
952 | |||
953 | unsigned char cu_read_key[32]; | ||
954 | unsigned int cu_read_key_len = sizeof(cu_write_key); | ||
955 | static const char READ_KEY_SALT_MDLD[] = "ReadKeySaltMDLD"; | ||
956 | static const char READ_KEY_INFO_MDLD[] = "ReadKeyInfoMDLD"; | ||
957 | hkdf_md(MD_ALGO_SHA512, (unsigned char*)READ_KEY_SALT_MDLD, sizeof(READ_KEY_SALT_MDLD)-1, (unsigned char*)READ_KEY_INFO_MDLD, sizeof(READ_KEY_INFO_MDLD)-1, client->cu_key, client->cu_key_len, cu_read_key, &cu_read_key_len); | ||
958 | |||
959 | // Starting with iOS/tvOS 11.2 and WatchOS 4.2, this nonce is random and sent along with the request. Before, the request doesn't have a nonce and it uses hardcoded nonce "sendone01234". | ||
960 | unsigned char cu_nonce[12] = "sendone01234"; // guaranteed to be random by fair dice troll | ||
961 | if (client->device->version >= DEVICE_VERSION(11,2,0)) { | ||
962 | #if defined(HAVE_OPENSSL) | ||
963 | RAND_bytes(cu_nonce, sizeof(cu_nonce)); | ||
964 | #elif defined(HAVE_GCRYPT) | ||
965 | gcry_create_nonce(cu_nonce, sizeof(cu_nonce)); | ||
966 | #endif | ||
967 | } | ||
968 | |||
969 | debug_plist(request_payload); | ||
970 | |||
971 | /* convert request payload to binary */ | ||
972 | uint32_t bin_len = 0; | ||
973 | char* bin = NULL; | ||
974 | plist_to_bin(request_payload, &bin, &bin_len); | ||
975 | |||
976 | /* encrypt request */ | ||
977 | size_t encrypted_len = bin_len + 16; | ||
978 | unsigned char* encrypted_buf = malloc(encrypted_len); | ||
979 | chacha20_poly1305_encrypt_96(cu_write_key, cu_nonce, NULL, 0, (unsigned char*)bin, bin_len, encrypted_buf, &encrypted_len); | ||
980 | free(bin); | ||
981 | bin = NULL; | ||
982 | |||
983 | plist_t dict = plist_new_dict(); | ||
984 | plist_dict_set_item(dict,"Request", plist_new_string(request)); | ||
985 | plist_dict_set_item(dict, "Payload", plist_new_data((char*)encrypted_buf, encrypted_len)); | ||
986 | free(encrypted_buf); | ||
987 | plist_dict_set_item(dict, "Nonce", plist_new_data((char*)cu_nonce, sizeof(cu_nonce))); | ||
988 | plist_dict_set_item(dict, "Label", plist_new_string(client->label)); | ||
989 | plist_dict_set_item(dict, "ProtocolVersion", plist_new_uint(2)); | ||
990 | |||
991 | /* send to device */ | ||
992 | ret = lockdownd_send(client, dict); | ||
993 | plist_free(dict); | ||
994 | dict = NULL; | ||
995 | |||
996 | if (ret != LOCKDOWN_E_SUCCESS) | ||
997 | return ret; | ||
998 | |||
999 | /* Now get device's answer */ | ||
1000 | ret = lockdownd_receive(client, &dict); | ||
1001 | if (ret != LOCKDOWN_E_SUCCESS) | ||
1002 | return ret; | ||
1003 | |||
1004 | ret = lockdown_check_result(dict, request); | ||
1005 | if (ret != LOCKDOWN_E_SUCCESS) { | ||
1006 | plist_free(dict); | ||
1007 | return ret; | ||
1008 | } | ||
1009 | |||
1010 | /* get payload */ | ||
1011 | plist_t blob = plist_dict_get_item(dict, "Payload"); | ||
1012 | if (!blob) { | ||
1013 | plist_free(dict); | ||
1014 | return LOCKDOWN_E_DICT_ERROR; | ||
1015 | } | ||
1016 | |||
1017 | uint64_t dl = 0; | ||
1018 | const char* dt = plist_get_data_ptr(blob, &dl); | ||
1019 | |||
1020 | /* see if we have a nonce */ | ||
1021 | blob = plist_dict_get_item(dict, "Nonce"); | ||
1022 | const unsigned char* rnonce = (unsigned char*)"receiveone01"; | ||
1023 | if (blob) { | ||
1024 | uint64_t rl = 0; | ||
1025 | rnonce = (const unsigned char*)plist_get_data_ptr(blob, &rl); | ||
1026 | } | ||
1027 | |||
1028 | /* decrypt payload */ | ||
1029 | size_t decrypted_len = dl-16; | ||
1030 | unsigned char* decrypted = malloc(decrypted_len); | ||
1031 | chacha20_poly1305_decrypt_96(cu_read_key, (unsigned char*)rnonce, NULL, 0, (unsigned char*)dt, dl, decrypted, &decrypted_len); | ||
1032 | plist_free(dict); | ||
1033 | dict = NULL; | ||
1034 | |||
1035 | plist_from_memory((const char*)decrypted, decrypted_len, &dict, NULL); | ||
1036 | if (!dict) { | ||
1037 | ret = LOCKDOWN_E_PLIST_ERROR; | ||
1038 | debug_info("Failed to parse PLIST from decrypted payload:"); | ||
1039 | debug_buffer((const char*)decrypted, decrypted_len); | ||
1040 | free(decrypted); | ||
1041 | return ret; | ||
1042 | } | ||
1043 | free(decrypted); | ||
1044 | |||
1045 | debug_plist(dict); | ||
1046 | |||
1047 | if (reply) { | ||
1048 | *reply = dict; | ||
1049 | } else { | ||
1050 | plist_free(dict); | ||
1051 | } | ||
1052 | |||
1053 | return LOCKDOWN_E_SUCCESS; | ||
1054 | #else | ||
1055 | debug_info("not supported"); | ||
1056 | return LOCKDOWN_E_UNKNOWN_ERROR; | ||
1057 | #endif | ||
1058 | } | ||
1059 | |||
1060 | lockdownd_error_t lockdownd_get_value_cu(lockdownd_client_t client, const char* domain, const char* key, plist_t* value) | ||
1061 | { | ||
1062 | #ifdef HAVE_WIRELESS_PAIRING | ||
1063 | if (!client) | ||
1064 | return LOCKDOWN_E_INVALID_ARG; | ||
1065 | |||
1066 | if (!client->cu_key) | ||
1067 | return LOCKDOWN_E_NO_RUNNING_SESSION; | ||
1068 | |||
1069 | lockdownd_error_t ret = LOCKDOWN_E_UNKNOWN_ERROR; | ||
1070 | |||
1071 | plist_t request = plist_new_dict(); | ||
1072 | if (domain) { | ||
1073 | plist_dict_set_item(request, "Domain", plist_new_string(domain)); | ||
1074 | } | ||
1075 | if (key) { | ||
1076 | plist_dict_set_item(request, "Key", plist_new_string(key)); | ||
1077 | } | ||
1078 | |||
1079 | plist_t reply = NULL; | ||
1080 | ret = lockdownd_cu_send_request_and_get_reply(client, "GetValueCU", request, &reply); | ||
1081 | plist_free(request); | ||
1082 | if (ret != LOCKDOWN_E_SUCCESS) { | ||
1083 | return ret; | ||
1084 | } | ||
1085 | |||
1086 | plist_t value_node = plist_dict_get_item(reply, "Value"); | ||
1087 | if (value_node) { | ||
1088 | debug_info("has a value"); | ||
1089 | *value = plist_copy(value_node); | ||
1090 | } | ||
1091 | plist_free(reply); | ||
1092 | |||
1093 | return ret; | ||
1094 | #else | ||
1095 | debug_info("not supported"); | ||
1096 | return LOCKDOWN_E_UNKNOWN_ERROR; | ||
1097 | #endif | ||
1098 | } | ||
1099 | |||
1100 | lockdownd_error_t lockdownd_pair_cu(lockdownd_client_t client) | ||
1101 | { | ||
1102 | #ifdef HAVE_WIRELESS_PAIRING | ||
1103 | if (!client) | ||
1104 | return LOCKDOWN_E_INVALID_ARG; | ||
1105 | |||
1106 | if (!client->cu_key) | ||
1107 | return LOCKDOWN_E_NO_RUNNING_SESSION; | ||
1108 | |||
1109 | lockdownd_error_t ret; | ||
1110 | |||
1111 | plist_t wifi_mac = NULL; | ||
1112 | ret = lockdownd_get_value_cu(client, NULL, "WiFiAddress", &wifi_mac); | ||
1113 | if (ret != LOCKDOWN_E_SUCCESS) { | ||
1114 | return ret; | ||
1115 | } | ||
1116 | |||
1117 | plist_t pubkey = NULL; | ||
1118 | ret = lockdownd_get_value_cu(client, NULL, "DevicePublicKey", &pubkey); | ||
1119 | if (ret != LOCKDOWN_E_SUCCESS) { | ||
1120 | plist_free(wifi_mac); | ||
1121 | return ret; | ||
1122 | } | ||
1123 | |||
1124 | key_data_t public_key = { NULL, 0 }; | ||
1125 | uint64_t data_len = 0; | ||
1126 | plist_get_data_val(pubkey, (char**)&public_key.data, &data_len); | ||
1127 | public_key.size = (unsigned int)data_len; | ||
1128 | plist_free(pubkey); | ||
1129 | |||
1130 | plist_t pair_record_plist = plist_new_dict(); | ||
1131 | pair_record_generate_keys_and_certs(pair_record_plist, public_key); | ||
1132 | |||
1133 | char* host_id = NULL; | ||
1134 | char* system_buid = NULL; | ||
1135 | |||
1136 | /* set SystemBUID */ | ||
1137 | userpref_read_system_buid(&system_buid); | ||
1138 | if (system_buid) { | ||
1139 | plist_dict_set_item(pair_record_plist, USERPREF_SYSTEM_BUID_KEY, plist_new_string(system_buid)); | ||
1140 | free(system_buid); | ||
1141 | } | ||
1142 | |||
1143 | /* set HostID */ | ||
1144 | host_id = generate_uuid(); | ||
1145 | pair_record_set_host_id(pair_record_plist, host_id); | ||
1146 | free(host_id); | ||
1147 | |||
1148 | plist_t request_pair_record = plist_copy(pair_record_plist); | ||
1149 | /* remove stuff that is private */ | ||
1150 | plist_dict_remove_item(request_pair_record, USERPREF_ROOT_PRIVATE_KEY_KEY); | ||
1151 | plist_dict_remove_item(request_pair_record, USERPREF_HOST_PRIVATE_KEY_KEY); | ||
1152 | |||
1153 | plist_t request = plist_new_dict(); | ||
1154 | plist_dict_set_item(request, "PairRecord", request_pair_record); | ||
1155 | plist_t pairing_opts = plist_new_dict(); | ||
1156 | plist_dict_set_item(pairing_opts, "ExtendedPairingErrors", plist_new_bool(1)); | ||
1157 | plist_dict_set_item(request, "PairingOptions", pairing_opts); | ||
1158 | |||
1159 | plist_t reply = NULL; | ||
1160 | ret = lockdownd_cu_send_request_and_get_reply(client, "PairCU", request, &reply); | ||
1161 | plist_free(request); | ||
1162 | if (ret != LOCKDOWN_E_SUCCESS) { | ||
1163 | plist_free(wifi_mac); | ||
1164 | return ret; | ||
1165 | } | ||
1166 | |||
1167 | char *s_udid = NULL; | ||
1168 | plist_t p_udid = plist_dict_get_item(reply, "UDID"); | ||
1169 | if (p_udid) { | ||
1170 | plist_get_string_val(p_udid, &s_udid); | ||
1171 | } | ||
1172 | plist_t ebag = plist_dict_get_item(reply, "EscrowBag"); | ||
1173 | if (ebag) { | ||
1174 | plist_dict_set_item(pair_record_plist, USERPREF_ESCROW_BAG_KEY, plist_copy(ebag)); | ||
1175 | } | ||
1176 | plist_dict_set_item(pair_record_plist, USERPREF_WIFI_MAC_ADDRESS_KEY, wifi_mac); | ||
1177 | plist_free(reply); | ||
1178 | |||
1179 | if (userpref_save_pair_record(s_udid, 0, pair_record_plist) != 0) { | ||
1180 | printf("Failed to save pair record for UDID %s\n", s_udid); | ||
1181 | } | ||
1182 | free(s_udid); | ||
1183 | s_udid = NULL; | ||
1184 | plist_free(pair_record_plist); | ||
1185 | |||
1186 | ret = LOCKDOWN_E_SUCCESS; | ||
1187 | |||
1188 | return ret; | ||
1189 | #else | ||
1190 | debug_info("not supported"); | ||
1191 | return LOCKDOWN_E_UNKNOWN_ERROR; | ||
1192 | #endif | ||
1193 | } | ||