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