appium-ios-remotexpc 0.21.1 → 0.22.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +13 -0
- package/build/src/lib/apple-tv/constants.d.ts +4 -3
- package/build/src/lib/apple-tv/constants.d.ts.map +1 -1
- package/build/src/lib/apple-tv/constants.js +10 -3
- package/build/src/lib/apple-tv/discovery/device-discovery.d.ts.map +1 -1
- package/build/src/lib/apple-tv/discovery/device-discovery.js +2 -2
- package/build/src/lib/apple-tv/encryption/index.d.ts +1 -0
- package/build/src/lib/apple-tv/encryption/index.d.ts.map +1 -1
- package/build/src/lib/apple-tv/encryption/index.js +1 -0
- package/build/src/lib/apple-tv/encryption/x25519.d.ts +8 -0
- package/build/src/lib/apple-tv/encryption/x25519.d.ts.map +1 -0
- package/build/src/lib/apple-tv/encryption/x25519.js +52 -0
- package/build/src/lib/apple-tv/index.d.ts +1 -0
- package/build/src/lib/apple-tv/index.d.ts.map +1 -1
- package/build/src/lib/apple-tv/index.js +1 -0
- package/build/src/lib/apple-tv/network/network-client.js +2 -2
- package/build/src/lib/apple-tv/pairing/user-input-service.d.ts.map +1 -1
- package/build/src/lib/apple-tv/pairing/user-input-service.js +2 -2
- package/build/src/lib/apple-tv/pairing-protocol/constants.d.ts +17 -0
- package/build/src/lib/apple-tv/pairing-protocol/constants.d.ts.map +1 -1
- package/build/src/lib/apple-tv/pairing-protocol/constants.js +25 -0
- package/build/src/lib/apple-tv/pairing-protocol/index.d.ts +2 -1
- package/build/src/lib/apple-tv/pairing-protocol/index.d.ts.map +1 -1
- package/build/src/lib/apple-tv/pairing-protocol/index.js +2 -1
- package/build/src/lib/apple-tv/pairing-protocol/pair-verification-protocol.d.ts +66 -0
- package/build/src/lib/apple-tv/pairing-protocol/pair-verification-protocol.d.ts.map +1 -0
- package/build/src/lib/apple-tv/pairing-protocol/pair-verification-protocol.js +178 -0
- package/build/src/lib/apple-tv/pairing-protocol/pairing-protocol.d.ts +35 -2
- package/build/src/lib/apple-tv/pairing-protocol/pairing-protocol.d.ts.map +1 -1
- package/build/src/lib/apple-tv/pairing-protocol/pairing-protocol.js +48 -14
- package/build/src/lib/apple-tv/storage/index.d.ts +1 -1
- package/build/src/lib/apple-tv/storage/index.d.ts.map +1 -1
- package/build/src/lib/apple-tv/storage/pairing-storage.d.ts +4 -1
- package/build/src/lib/apple-tv/storage/pairing-storage.d.ts.map +1 -1
- package/build/src/lib/apple-tv/storage/pairing-storage.js +59 -4
- package/build/src/lib/apple-tv/storage/types.d.ts +7 -1
- package/build/src/lib/apple-tv/storage/types.d.ts.map +1 -1
- package/build/src/lib/apple-tv/tunnel/index.d.ts +3 -0
- package/build/src/lib/apple-tv/tunnel/index.d.ts.map +1 -0
- package/build/src/lib/apple-tv/tunnel/index.js +1 -0
- package/build/src/lib/apple-tv/tunnel/tunnel-service.d.ts +57 -0
- package/build/src/lib/apple-tv/tunnel/tunnel-service.d.ts.map +1 -0
- package/build/src/lib/apple-tv/tunnel/tunnel-service.js +357 -0
- package/build/src/lib/apple-tv/tunnel/types.d.ts +22 -0
- package/build/src/lib/apple-tv/tunnel/types.d.ts.map +1 -0
- package/build/src/lib/apple-tv/tunnel/types.js +1 -0
- package/build/src/lib/bonjour/bonjour-discovery.d.ts.map +1 -1
- package/build/src/lib/bonjour/bonjour-discovery.js +3 -3
- package/build/src/lib/lockdown/index.d.ts.map +1 -1
- package/build/src/lib/plist/length-based-splitter.d.ts.map +1 -1
- package/build/src/lib/plist/length-based-splitter.js +0 -7
- package/build/src/lib/tunnel/index.d.ts +1 -0
- package/build/src/lib/tunnel/index.d.ts.map +1 -1
- package/build/src/lib/tunnel/packet-stream-server.d.ts.map +1 -1
- package/build/src/lib/tunnel/tunnel-registry-server.d.ts +1 -0
- package/build/src/lib/tunnel/tunnel-registry-server.d.ts.map +1 -1
- package/build/src/lib/tunnel/tunnel-registry-server.js +1 -1
- package/build/src/services/ios/afc/index.d.ts.map +1 -1
- package/build/src/services/ios/afc/index.js +14 -2
- package/build/src/services/ios/afc/stream-utils.d.ts.map +1 -1
- package/build/src/services/ios/afc/stream-utils.js +0 -2
- package/build/src/services/ios/mobile-config/index.js +2 -2
- package/package.json +2 -1
- package/scripts/pair-appletv.ts +2 -2
- package/scripts/start-appletv-tunnel.ts +178 -0
- package/scripts/test-tunnel-creation.ts +32 -23
- package/src/lib/apple-tv/constants.ts +11 -3
- package/src/lib/apple-tv/discovery/device-discovery.ts +2 -3
- package/src/lib/apple-tv/encryption/index.ts +6 -0
- package/src/lib/apple-tv/encryption/x25519.ts +79 -0
- package/src/lib/apple-tv/index.ts +1 -0
- package/src/lib/apple-tv/network/network-client.ts +2 -2
- package/src/lib/apple-tv/pairing/user-input-service.ts +2 -2
- package/src/lib/apple-tv/pairing-protocol/constants.ts +29 -0
- package/src/lib/apple-tv/pairing-protocol/index.ts +12 -1
- package/src/lib/apple-tv/pairing-protocol/pair-verification-protocol.ts +329 -0
- package/src/lib/apple-tv/pairing-protocol/pairing-protocol.ts +49 -19
- package/src/lib/apple-tv/storage/index.ts +1 -1
- package/src/lib/apple-tv/storage/pairing-storage.ts +73 -5
- package/src/lib/apple-tv/storage/types.ts +8 -1
- package/src/lib/apple-tv/tunnel/index.ts +2 -0
- package/src/lib/apple-tv/tunnel/tunnel-service.ts +543 -0
- package/src/lib/apple-tv/tunnel/types.ts +23 -0
- package/src/lib/bonjour/bonjour-discovery.ts +3 -5
- package/src/lib/lockdown/index.ts +0 -7
- package/src/lib/plist/length-based-splitter.ts +0 -22
- package/src/lib/tunnel/index.ts +2 -8
- package/src/lib/tunnel/packet-stream-server.ts +0 -8
- package/src/lib/tunnel/tunnel-registry-server.ts +1 -1
- package/src/services/ios/afc/index.ts +14 -3
- package/src/services/ios/afc/stream-utils.ts +0 -2
- package/src/services/ios/mobile-config/index.ts +2 -2
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { logger } from '@appium/support';
|
|
2
1
|
import { randomBytes } from 'node:crypto';
|
|
3
2
|
import { hostname } from 'node:os';
|
|
3
|
+
import { getLogger } from '../../logger.js';
|
|
4
4
|
import { DEFAULT_PAIRING_CONFIG, PairingDataComponentType, } from '../constants.js';
|
|
5
5
|
import { encodeAppleTVDeviceInfo } from '../deviceInfo/index.js';
|
|
6
6
|
import { createEd25519Signature, decryptChaCha20Poly1305, encryptChaCha20Poly1305, generateEd25519KeyPair, hkdf, } from '../encryption/index.js';
|
|
@@ -11,11 +11,45 @@ import { PairingStorage } from '../storage/pairing-storage.js';
|
|
|
11
11
|
import { createPairVerificationData, createSetupManualPairingData, decodeTLV8ToDict, encodeTLV8, } from '../tlv/index.js';
|
|
12
12
|
import { generateHostId } from '../utils/uuid-generator.js';
|
|
13
13
|
import { INFO_TYPE, PAIRING_MESSAGES, PAIRING_STATES } from './constants.js';
|
|
14
|
-
|
|
14
|
+
const log = getLogger('PairingProtocol');
|
|
15
|
+
/**
|
|
16
|
+
* Implements the HomeKit Accessory Protocol (HAP) Pair-Setup process for Apple TV.
|
|
17
|
+
*
|
|
18
|
+
* Protocol Overview:
|
|
19
|
+
* This class implements the HAP Pair-Setup protocol, which establishes a secure pairing
|
|
20
|
+
* between a controller (this client) and an Apple TV accessory. The protocol uses SRP
|
|
21
|
+
* (Secure Remote Password) authentication to verify a user-provided PIN without transmitting
|
|
22
|
+
* the PIN itself over the network.
|
|
23
|
+
*
|
|
24
|
+
* Message Exchange Flow (M1-M6):
|
|
25
|
+
* - M1/M2: Initial setup request and SRP challenge (salt + server public key)
|
|
26
|
+
* - M3/M4: Client sends SRP proof, server validates and responds
|
|
27
|
+
* - M5/M6: Exchange of long-term public keys and signatures (encrypted)
|
|
28
|
+
*
|
|
29
|
+
* After successful pairing, the generated Ed25519 key pair is stored and used for
|
|
30
|
+
* subsequent Pair-Verify operations to establish encrypted sessions.
|
|
31
|
+
*
|
|
32
|
+
* Technical Details:
|
|
33
|
+
* - Uses SRP-6a protocol for password-authenticated key exchange (RFC 5054)
|
|
34
|
+
* - Employs Ed25519 for long-term identity keys (RFC 8032)
|
|
35
|
+
* - Uses ChaCha20-Poly1305 for authenticated encryption (RFC 8439)
|
|
36
|
+
* - Derives session keys using HKDF (RFC 5869)
|
|
37
|
+
* - Encodes messages in TLV8 (Type-Length-Value) format
|
|
38
|
+
*
|
|
39
|
+
* References:
|
|
40
|
+
* - HAP Specification: https://developer.apple.com/homekit/ (Apple Developer)
|
|
41
|
+
* - HAP-NodeJS (community implementation): https://github.com/homebridge/HAP-NodeJS
|
|
42
|
+
* - SRP Protocol: https://datatracker.ietf.org/doc/html/rfc5054
|
|
43
|
+
* - Ed25519: https://datatracker.ietf.org/doc/html/rfc8032
|
|
44
|
+
* - ChaCha20-Poly1305: https://datatracker.ietf.org/doc/html/rfc8439
|
|
45
|
+
* - HKDF: https://datatracker.ietf.org/doc/html/rfc5869
|
|
46
|
+
*
|
|
47
|
+
* @see PairVerificationProtocol for the verification protocol used after pairing
|
|
48
|
+
* @see SRPClient for SRP authentication implementation
|
|
49
|
+
*/
|
|
15
50
|
export class PairingProtocol {
|
|
16
51
|
networkClient;
|
|
17
52
|
userInput;
|
|
18
|
-
static log = logger.getLogger('PairingProtocol');
|
|
19
53
|
_sequenceNumber = 0;
|
|
20
54
|
constructor(networkClient, userInput) {
|
|
21
55
|
this.networkClient = networkClient;
|
|
@@ -43,7 +77,7 @@ export class PairingProtocol {
|
|
|
43
77
|
return this.createPairingResult(device, ltpk, ltsk);
|
|
44
78
|
}
|
|
45
79
|
catch (error) {
|
|
46
|
-
|
|
80
|
+
log.error('Pairing flow failed:', error);
|
|
47
81
|
throw error;
|
|
48
82
|
}
|
|
49
83
|
}
|
|
@@ -90,7 +124,7 @@ export class PairingProtocol {
|
|
|
90
124
|
const request = this.createHandshakeRequest();
|
|
91
125
|
await this.networkClient.sendPacket(request);
|
|
92
126
|
await this.networkClient.receiveResponse();
|
|
93
|
-
|
|
127
|
+
log.debug('Handshake completed');
|
|
94
128
|
}
|
|
95
129
|
/**
|
|
96
130
|
* Attempts to verify existing pairing credentials with Apple TV
|
|
@@ -102,7 +136,7 @@ export class PairingProtocol {
|
|
|
102
136
|
await this.networkClient.receiveResponse();
|
|
103
137
|
const failedRequest = this.createPairVerifyFailedRequest();
|
|
104
138
|
await this.networkClient.sendPacket(failedRequest);
|
|
105
|
-
|
|
139
|
+
log.debug('Pair verification attempt completed');
|
|
106
140
|
}
|
|
107
141
|
/**
|
|
108
142
|
* Initiates manual pairing setup process with Apple TV
|
|
@@ -113,7 +147,7 @@ export class PairingProtocol {
|
|
|
113
147
|
const request = this.createSetupManualPairingRequest();
|
|
114
148
|
await this.networkClient.sendPacket(request);
|
|
115
149
|
const response = await this.networkClient.receiveResponse();
|
|
116
|
-
|
|
150
|
+
log.debug('Manual pairing setup completed');
|
|
117
151
|
return response;
|
|
118
152
|
}
|
|
119
153
|
/**
|
|
@@ -144,7 +178,7 @@ export class PairingProtocol {
|
|
|
144
178
|
await this.sendSRPProof(srpClient);
|
|
145
179
|
const response = await this.networkClient.receiveResponse();
|
|
146
180
|
this.validateSRPProofResponse(response);
|
|
147
|
-
|
|
181
|
+
log.debug('SRP authentication completed');
|
|
148
182
|
return srpClient;
|
|
149
183
|
}
|
|
150
184
|
/**
|
|
@@ -154,12 +188,12 @@ export class PairingProtocol {
|
|
|
154
188
|
*/
|
|
155
189
|
async receiveM6Completion(decryptKey) {
|
|
156
190
|
const m6Response = await this.networkClient.receiveResponse();
|
|
157
|
-
|
|
191
|
+
log.info('M6 Response received');
|
|
158
192
|
try {
|
|
159
193
|
this.processM6Response(m6Response, decryptKey);
|
|
160
194
|
}
|
|
161
195
|
catch (error) {
|
|
162
|
-
|
|
196
|
+
log.warn('M6 decryption failed - but pairing may still be successful:', error.message);
|
|
163
197
|
}
|
|
164
198
|
}
|
|
165
199
|
/**
|
|
@@ -443,10 +477,10 @@ export class PairingProtocol {
|
|
|
443
477
|
const m6DataBase64 = m6Response.message.plain._0.event._0.pairingData._0.data;
|
|
444
478
|
const m6TLVBuffer = Buffer.from(m6DataBase64, 'base64');
|
|
445
479
|
const m6Parsed = decodeTLV8ToDict(m6TLVBuffer);
|
|
446
|
-
|
|
480
|
+
log.debug('M6 TLV types received:', Object.keys(m6Parsed).map((k) => `0x${Number(k).toString(16)}`));
|
|
447
481
|
const stateData = m6Parsed[PairingDataComponentType.STATE];
|
|
448
482
|
if (stateData && stateData[0] === PAIRING_STATES.M6) {
|
|
449
|
-
|
|
483
|
+
log.info('✅ Pairing completed successfully (STATE=0x06)');
|
|
450
484
|
}
|
|
451
485
|
const encryptedData = m6Parsed[PairingDataComponentType.ENCRYPTED_DATA];
|
|
452
486
|
if (encryptedData) {
|
|
@@ -457,7 +491,7 @@ export class PairingProtocol {
|
|
|
457
491
|
nonce,
|
|
458
492
|
});
|
|
459
493
|
const decryptedTLV = decodeTLV8ToDict(decrypted);
|
|
460
|
-
|
|
494
|
+
log.debug('M6 decrypted content types:', Object.keys(decryptedTLV));
|
|
461
495
|
}
|
|
462
496
|
}
|
|
463
497
|
/**
|
|
@@ -473,7 +507,7 @@ export class PairingProtocol {
|
|
|
473
507
|
info: Buffer.from(PAIRING_MESSAGES.ENCRYPT_INFO, 'utf8'),
|
|
474
508
|
length: 32,
|
|
475
509
|
});
|
|
476
|
-
|
|
510
|
+
log.debug('Derived encryption keys');
|
|
477
511
|
return {
|
|
478
512
|
encryptKey: sharedKey,
|
|
479
513
|
decryptKey: sharedKey,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../src/lib/apple-tv/storage/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AACtD,YAAY,EAAE,uBAAuB,EAAE,MAAM,YAAY,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../src/lib/apple-tv/storage/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AACtD,YAAY,EAAE,uBAAuB,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC"}
|
|
@@ -1,12 +1,15 @@
|
|
|
1
1
|
import type { PairingConfig } from '../types.js';
|
|
2
|
-
import type { PairingStorageInterface } from './types.js';
|
|
2
|
+
import type { PairRecord, PairingStorageInterface } from './types.js';
|
|
3
3
|
/** Manages persistent storage of pairing credentials as plist files */
|
|
4
4
|
export declare class PairingStorage implements PairingStorageInterface {
|
|
5
5
|
private readonly config;
|
|
6
6
|
private readonly log;
|
|
7
7
|
private readonly box;
|
|
8
|
+
private strongboxDir?;
|
|
8
9
|
constructor(config: PairingConfig);
|
|
9
10
|
save(deviceId: string, ltpk: Buffer, ltsk: Buffer, remoteUnlockHostKey?: string): Promise<string>;
|
|
11
|
+
load(deviceId: string): Promise<PairRecord | null>;
|
|
12
|
+
getAvailableDeviceIds(): Promise<string[]>;
|
|
10
13
|
private createPlistContent;
|
|
11
14
|
}
|
|
12
15
|
//# sourceMappingURL=pairing-storage.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"pairing-storage.d.ts","sourceRoot":"","sources":["../../../../../src/lib/apple-tv/storage/pairing-storage.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"pairing-storage.d.ts","sourceRoot":"","sources":["../../../../../src/lib/apple-tv/storage/pairing-storage.ts"],"names":[],"mappings":"AASA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AACjD,OAAO,KAAK,EAAE,UAAU,EAAE,uBAAuB,EAAE,MAAM,YAAY,CAAC;AAEtE,uEAAuE;AACvE,qBAAa,cAAe,YAAW,uBAAuB;IAKhD,OAAO,CAAC,QAAQ,CAAC,MAAM;IAJnC,OAAO,CAAC,QAAQ,CAAC,GAAG,CAA+B;IACnD,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAC;IACrB,OAAO,CAAC,YAAY,CAAC,CAAS;gBAED,MAAM,EAAE,aAAa;IAI5C,IAAI,CACR,QAAQ,EAAE,MAAM,EAChB,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,MAAM,EACZ,mBAAmB,SAAK,GACvB,OAAO,CAAC,MAAM,CAAC;IAyBZ,IAAI,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,GAAG,IAAI,CAAC;IAwClD,qBAAqB,IAAI,OAAO,CAAC,MAAM,EAAE,CAAC;IAwBhD,OAAO,CAAC,kBAAkB;CAW3B"}
|
|
@@ -1,20 +1,24 @@
|
|
|
1
1
|
import { strongbox } from '@appium/strongbox';
|
|
2
|
-
import {
|
|
2
|
+
import { readdir } from 'node:fs/promises';
|
|
3
|
+
import { dirname } from 'node:path';
|
|
3
4
|
import { STRONGBOX_CONTAINER_NAME } from '../../../constants.js';
|
|
4
|
-
import {
|
|
5
|
+
import { getLogger } from '../../logger.js';
|
|
6
|
+
import { createXmlPlist, parseXmlPlist } from '../../plist/index.js';
|
|
7
|
+
import { APPLETV_PAIRING_PREFIX } from '../constants.js';
|
|
5
8
|
import { PairingError } from '../errors.js';
|
|
6
9
|
/** Manages persistent storage of pairing credentials as plist files */
|
|
7
10
|
export class PairingStorage {
|
|
8
11
|
config;
|
|
9
|
-
log =
|
|
12
|
+
log = getLogger('PairingStorage');
|
|
10
13
|
box;
|
|
14
|
+
strongboxDir;
|
|
11
15
|
constructor(config) {
|
|
12
16
|
this.config = config;
|
|
13
17
|
this.box = strongbox(STRONGBOX_CONTAINER_NAME);
|
|
14
18
|
}
|
|
15
19
|
async save(deviceId, ltpk, ltsk, remoteUnlockHostKey = '') {
|
|
16
20
|
try {
|
|
17
|
-
const itemName =
|
|
21
|
+
const itemName = `${APPLETV_PAIRING_PREFIX}${deviceId}`;
|
|
18
22
|
const plistContent = this.createPlistContent(ltpk, ltsk, remoteUnlockHostKey);
|
|
19
23
|
const item = await this.box.createItemWithValue(itemName, plistContent);
|
|
20
24
|
const itemPath = item.id;
|
|
@@ -26,6 +30,57 @@ export class PairingStorage {
|
|
|
26
30
|
throw new PairingError('Failed to save pairing record', 'SAVE_ERROR', error);
|
|
27
31
|
}
|
|
28
32
|
}
|
|
33
|
+
async load(deviceId) {
|
|
34
|
+
const itemName = `${APPLETV_PAIRING_PREFIX}${deviceId}`;
|
|
35
|
+
try {
|
|
36
|
+
const item = this.box.getItem(itemName) ?? (await this.box.createItem(itemName));
|
|
37
|
+
const pairingData = await item.read();
|
|
38
|
+
if (!pairingData) {
|
|
39
|
+
this.log.debug(`No pair record found for device ${deviceId}`);
|
|
40
|
+
return null;
|
|
41
|
+
}
|
|
42
|
+
const parsed = parseXmlPlist(pairingData);
|
|
43
|
+
if (!parsed.private_key || !parsed.public_key) {
|
|
44
|
+
throw new Error('Could not parse pairing record keys');
|
|
45
|
+
}
|
|
46
|
+
const privateKey = Buffer.isBuffer(parsed.private_key)
|
|
47
|
+
? parsed.private_key
|
|
48
|
+
: Buffer.from(parsed.private_key, 'base64');
|
|
49
|
+
const publicKey = Buffer.isBuffer(parsed.public_key)
|
|
50
|
+
? parsed.public_key
|
|
51
|
+
: Buffer.from(parsed.public_key, 'base64');
|
|
52
|
+
this.log.debug(`Loaded pair record for ${deviceId}`);
|
|
53
|
+
return {
|
|
54
|
+
privateKey,
|
|
55
|
+
publicKey,
|
|
56
|
+
remoteUnlockHostKey: parsed.remote_unlock_host_key || '',
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
catch (error) {
|
|
60
|
+
this.log.error(`Failed to load pair record for ${deviceId}:`, error);
|
|
61
|
+
return null;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
async getAvailableDeviceIds() {
|
|
65
|
+
try {
|
|
66
|
+
if (!this.strongboxDir) {
|
|
67
|
+
const dummyItem = await this.box.createItem('_temp');
|
|
68
|
+
this.strongboxDir = dirname(dummyItem.id);
|
|
69
|
+
// Clean up the temporary item after extracting the directory path
|
|
70
|
+
await dummyItem.clear();
|
|
71
|
+
}
|
|
72
|
+
const files = await readdir(this.strongboxDir);
|
|
73
|
+
const deviceIds = files
|
|
74
|
+
.filter((file) => file.startsWith(APPLETV_PAIRING_PREFIX))
|
|
75
|
+
.map((file) => file.replace(APPLETV_PAIRING_PREFIX, ''));
|
|
76
|
+
this.log.debug(`Found ${deviceIds.length} pair record(s): ${deviceIds.join(', ')}`);
|
|
77
|
+
return deviceIds;
|
|
78
|
+
}
|
|
79
|
+
catch (error) {
|
|
80
|
+
this.log.debug('Error getting available device IDs:', error);
|
|
81
|
+
return [];
|
|
82
|
+
}
|
|
83
|
+
}
|
|
29
84
|
createPlistContent(publicKey, privateKey, remoteUnlockHostKey) {
|
|
30
85
|
return createXmlPlist({
|
|
31
86
|
private_key: privateKey,
|
|
@@ -1,5 +1,11 @@
|
|
|
1
|
-
|
|
1
|
+
export interface PairRecord {
|
|
2
|
+
publicKey: Buffer;
|
|
3
|
+
privateKey: Buffer;
|
|
4
|
+
remoteUnlockHostKey: string;
|
|
5
|
+
}
|
|
2
6
|
export interface PairingStorageInterface {
|
|
3
7
|
save(deviceId: string, ltpk: Buffer, ltsk: Buffer, remoteUnlockHostKey?: string): Promise<string>;
|
|
8
|
+
load(deviceId: string): Promise<PairRecord | null>;
|
|
9
|
+
getAvailableDeviceIds(): Promise<string[]>;
|
|
4
10
|
}
|
|
5
11
|
//# sourceMappingURL=types.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../../../src/lib/apple-tv/storage/types.ts"],"names":[],"mappings":"AAAA,
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../../../src/lib/apple-tv/storage/types.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,UAAU;IACzB,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,MAAM,CAAC;IACnB,mBAAmB,EAAE,MAAM,CAAC;CAC7B;AAED,MAAM,WAAW,uBAAuB;IACtC,IAAI,CACF,QAAQ,EAAE,MAAM,EAChB,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,MAAM,EACZ,mBAAmB,CAAC,EAAE,MAAM,GAC3B,OAAO,CAAC,MAAM,CAAC,CAAC;IACnB,IAAI,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,GAAG,IAAI,CAAC,CAAC;IACnD,qBAAqB,IAAI,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC;CAC5C"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../src/lib/apple-tv/tunnel/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,oBAAoB,EAAE,MAAM,qBAAqB,CAAC;AAC1E,YAAY,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { TunnelService, AppleTVTunnelService } from './tunnel-service.js';
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import * as tls from 'node:tls';
|
|
2
|
+
import type { AppleTVDevice } from '../../bonjour/bonjour-discovery.js';
|
|
3
|
+
import type { NetworkClientInterface } from '../network/types.js';
|
|
4
|
+
import type { VerificationKeys } from '../pairing-protocol/pair-verification-protocol.js';
|
|
5
|
+
import type { TcpListenerInfo } from './types.js';
|
|
6
|
+
export declare class TunnelService {
|
|
7
|
+
private readonly networkClient;
|
|
8
|
+
private readonly keys;
|
|
9
|
+
private sequenceNumber;
|
|
10
|
+
private static readonly log;
|
|
11
|
+
private encryptedSequenceNumber;
|
|
12
|
+
constructor(networkClient: NetworkClientInterface, keys: VerificationKeys, sequenceNumber: number);
|
|
13
|
+
createTcpListener(): Promise<TcpListenerInfo>;
|
|
14
|
+
createTlsPskConnection(hostname: string, port: number): Promise<tls.TLSSocket>;
|
|
15
|
+
getSequenceNumber(): number;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* High-level service for establishing Apple TV tunnels.
|
|
19
|
+
* Orchestrates device discovery, pairing verification, and tunnel creation.
|
|
20
|
+
*/
|
|
21
|
+
export declare class AppleTVTunnelService {
|
|
22
|
+
private readonly networkClient;
|
|
23
|
+
private readonly storage;
|
|
24
|
+
private sequenceNumber;
|
|
25
|
+
constructor();
|
|
26
|
+
disconnect(): void;
|
|
27
|
+
startTunnel(deviceId?: string, specificDeviceIdentifier?: string): Promise<{
|
|
28
|
+
socket: tls.TLSSocket;
|
|
29
|
+
device: AppleTVDevice;
|
|
30
|
+
}>;
|
|
31
|
+
/**
|
|
32
|
+
* Logs information about discovered devices
|
|
33
|
+
*/
|
|
34
|
+
private logDiscoveredDevices;
|
|
35
|
+
/**
|
|
36
|
+
* Selects devices to process based on the specific device identifier
|
|
37
|
+
*/
|
|
38
|
+
private selectDevicesToProcess;
|
|
39
|
+
/**
|
|
40
|
+
* Validates pair records and returns the list of identifiers to try
|
|
41
|
+
*/
|
|
42
|
+
private validateAndGetPairRecords;
|
|
43
|
+
/**
|
|
44
|
+
* Attempts to establish a connection with a device using a specific pair record
|
|
45
|
+
*/
|
|
46
|
+
private attemptDeviceConnection;
|
|
47
|
+
/**
|
|
48
|
+
* Logs a summary of all failed connection attempts
|
|
49
|
+
*/
|
|
50
|
+
private logFailureSummary;
|
|
51
|
+
private discoverDevices;
|
|
52
|
+
private performHandshake;
|
|
53
|
+
private performPairVerification;
|
|
54
|
+
private createTcpListener;
|
|
55
|
+
private createTlsPskConnection;
|
|
56
|
+
}
|
|
57
|
+
//# sourceMappingURL=tunnel-service.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tunnel-service.d.ts","sourceRoot":"","sources":["../../../../../src/lib/apple-tv/tunnel/tunnel-service.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,GAAG,MAAM,UAAU,CAAC;AAEhC,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,oCAAoC,CAAC;AAUxE,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,qBAAqB,CAAC;AAElE,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,mDAAmD,CAAC;AAG1F,OAAO,KAAK,EAAE,eAAe,EAA2B,MAAM,YAAY,CAAC;AAI3E,qBAAa,aAAa;IAKtB,OAAO,CAAC,QAAQ,CAAC,aAAa;IAC9B,OAAO,CAAC,QAAQ,CAAC,IAAI;IACrB,OAAO,CAAC,cAAc;IANxB,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,GAAG,CAA8B;IACzD,OAAO,CAAC,uBAAuB,CAAK;gBAGjB,aAAa,EAAE,sBAAsB,EACrC,IAAI,EAAE,gBAAgB,EAC/B,cAAc,EAAE,MAAM;IAG1B,iBAAiB,IAAI,OAAO,CAAC,eAAe,CAAC;IAoF7C,sBAAsB,CAC1B,QAAQ,EAAE,MAAM,EAChB,IAAI,EAAE,MAAM,GACX,OAAO,CAAC,GAAG,CAAC,SAAS,CAAC;IAoEzB,iBAAiB,IAAI,MAAM;CAG5B;AAED;;;GAGG;AACH,qBAAa,oBAAoB;IAC/B,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAgB;IAC9C,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAiB;IACzC,OAAO,CAAC,cAAc,CAAK;;IAO3B,UAAU,IAAI,IAAI;IAIZ,WAAW,CACf,QAAQ,CAAC,EAAE,MAAM,EACjB,wBAAwB,CAAC,EAAE,MAAM,GAChC,OAAO,CAAC;QAAE,MAAM,EAAE,GAAG,CAAC,SAAS,CAAC;QAAC,MAAM,EAAE,aAAa,CAAA;KAAE,CAAC;IAiD5D;;OAEG;IACH,OAAO,CAAC,oBAAoB;IAkB5B;;OAEG;IACH,OAAO,CAAC,sBAAsB;IAgC9B;;OAEG;YACW,yBAAyB;IAgBvC;;OAEG;YACW,uBAAuB;IA2DrC;;OAEG;IACH,OAAO,CAAC,iBAAiB;YAWX,eAAe;YAoDf,gBAAgB;YA4BhB,uBAAuB;YAmBvB,iBAAiB;YAiBjB,sBAAsB;CAYrC"}
|