@basmilius/apple-companion-link 0.0.2 → 0.0.4

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.
@@ -0,0 +1,25 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../src/cli.ts", "../src/discovery/discovery.ts", "../src/const.ts", "../src/encoding/opack.ts", "../src/encoding/plist.ts", "../src/encoding/tlv8.ts", "../src/socket/companionLink.ts", "../src/crypto/chacha20.ts", "../src/crypto/curve25519.ts", "../src/crypto/hkdf.ts", "../src/socket/base.ts", "../src/protocol/api/companionLink.ts", "../src/protocol/pairing/companionLink.ts", "../src/protocol/verify/companionLink.ts", "../src/protocol/companionLink.ts", "../src/test.ts"],
4
+ "sourcesContent": [
5
+ "import { createInterface } from 'node:readline';\nimport { styleText } from 'node:util';\n\nconst stdin = createInterface({\n input: process.stdin,\n output: process.stdout\n});\n\nexport function debug(...data: any[]): void {\n console.debug(styleText('cyan', '[debug]'), ...data);\n}\n\nexport async function prompt(message: string): Promise<string> {\n return await new Promise<string>(resolve => stdin.question(`${message}: `, resolve));\n}\n\nexport async function waitFor(ms: number): Promise<void> {\n return new Promise(resolve => setTimeout(resolve, ms));\n}\n",
6
+ "import mdns, { Result } from 'node-dns-sd';\nimport { waitFor } from '@/cli';\nimport { AIRPLAY_SERVICE, COMPANION_LINK_SERVICE, RAOP_SERVICE } from '@/const';\n\nexport default class Discovery {\n readonly #service: string;\n\n constructor(service: string) {\n this.#service = service;\n }\n\n async find(): Promise<Result[]> {\n return await mdns.discover({\n name: this.#service\n });\n }\n\n async findUntil(fqdn: string, tries: number = 10, timeout: number = 1000): Promise<Result> {\n while (tries > 0) {\n const devices = await this.find();\n const device = devices.find(device => device.fqdn === fqdn);\n\n if (device) {\n return device;\n }\n\n console.log();\n console.log(`Device not found, retrying in ${timeout}ms...`);\n console.log(devices.map(d => ` ● ${d.fqdn}`).join('\\n'));\n\n tries--;\n\n if (tries === 0) {\n throw new Error('Device not found after serveral tries, aborting.');\n }\n\n await waitFor(timeout);\n }\n }\n\n static airplay(): Discovery {\n return new Discovery(AIRPLAY_SERVICE);\n }\n\n static companionLink(): Discovery {\n return new Discovery(COMPANION_LINK_SERVICE);\n }\n\n static raop(): Discovery {\n return new Discovery(RAOP_SERVICE);\n }\n}\n",
7
+ "export const AIRPLAY_TRANSIENT_PIN = '3939';\n\nexport const HTTP_TIMEOUT = 15000;\n\nexport const AIRPLAY_SERVICE = '_airplay._tcp.local';\nexport const COMPANION_LINK_SERVICE = '_companion-link._tcp.local';\nexport const RAOP_SERVICE = '_raop._tcp.local';\n",
8
+ "type Packed = Uint8Array;\ntype ObjectList = Packed[];\n\nclass SizedInt extends Number {\n size: number;\n\n constructor(value: number, size: number) {\n super(value);\n this.size = size;\n }\n}\n\nconst _SIZED_INT_TYPES: Record<number, typeof SizedInt> = {};\n\nexport function sizedInt(value: number, size: number): SizedInt {\n return new SizedInt(value, size);\n}\n\nclass OPACKFloat {\n value: number;\n\n constructor(value: number) {\n this.value = value;\n }\n}\n\nexport function float(value: number) {\n return new OPACKFloat(value);\n}\n\nclass OPACKInt {\n value: number;\n\n constructor(value: number) {\n this.value = value;\n }\n}\n\nexport function int(value: number) {\n return new OPACKInt(value);\n}\n\nfunction concat(arr: Uint8Array[]): Uint8Array {\n const total = arr.reduce((s, a) => s + a.length, 0);\n const out = new Uint8Array(total);\n let off = 0;\n for (const a of arr) {\n out.set(a, off);\n off += a.length;\n }\n return out;\n}\n\nfunction u8(b: number) {\n return Uint8Array.of(b);\n}\n\nfunction uintToLEBytes(value: number | bigint, byteLen: number): Uint8Array {\n const out = new Uint8Array(byteLen);\n let v = BigInt(value);\n for (let i = 0; i < byteLen; i++) {\n out[i] = Number(v & 0xffn);\n v >>= 8n;\n }\n return out;\n}\n\nfunction concatUint8Arrays(arrays: Uint8Array[]): Uint8Array {\n const total = arrays.reduce((sum, a) => sum + a.length, 0);\n const out = new Uint8Array(total);\n let offset = 0;\n for (const a of arrays) {\n out.set(a, offset);\n offset += a.length;\n }\n return out;\n}\n\nexport function pack(data: any): Uint8Array {\n return _pack(data, []);\n}\n\nfunction _pack(data: any, objectList: ObjectList): Uint8Array {\n let packed: Uint8Array | null = null;\n\n if (data === null || data === undefined) packed = u8(0x04);\n else if (typeof data === 'boolean') packed = u8(data ? 0x01 : 0x02);\n else if (data instanceof OPACKFloat) {\n const buf = new ArrayBuffer(8);\n new DataView(buf).setFloat64(0, data.value, true);\n packed = concat([u8(0x36), new Uint8Array(buf)]);\n } else if (data instanceof OPACKInt) {\n const val = data.value;\n if (val < 0x28) packed = u8(0x08 + val);\n else if (val <= 0xff) packed = concatUint8Arrays([u8(0x30), uintToLEBytes(val, 1)]);\n else if (val <= 0xffff) packed = concatUint8Arrays([u8(0x31), uintToLEBytes(val, 2)]);\n else if (val <= 0xffffffff) packed = concatUint8Arrays([u8(0x32), uintToLEBytes(val, 4)]);\n else packed = concatUint8Arrays([u8(0x33), uintToLEBytes(val, 8)]);\n } else if (typeof data === 'number') {\n if (!Number.isInteger(data)) {\n const buf = new ArrayBuffer(8);\n new DataView(buf).setFloat64(0, data, true);\n packed = concat([u8(0x36), new Uint8Array(buf)]);\n } else {\n if (data < 0x28) packed = u8(0x08 + data);\n else if (data <= 0xff) packed = concat([u8(0x30), uintToLEBytes(data, 1)]);\n else if (data <= 0xffff) packed = concat([u8(0x31), uintToLEBytes(data, 2)]);\n else if (data <= 0xffffffff) packed = concat([u8(0x32), uintToLEBytes(data, 4)]);\n else packed = concat([u8(0x33), uintToLEBytes(data, 8)]);\n }\n } else if (data instanceof SizedInt) {\n packed = concat([u8(0x30 + Math.log2(data.size)), uintToLEBytes(data.valueOf(), data.size)]);\n } else if (typeof data === 'string') {\n const b = new TextEncoder().encode(data);\n const len = b.length;\n if (len <= 0x20) packed = concat([u8(0x40 + len), b]);\n else if (len <= 0xff) packed = concat([u8(0x61), uintToLEBytes(len, 1), b]);\n else if (len <= 0xffff) packed = concat([u8(0x62), uintToLEBytes(len, 2), b]);\n else if (len <= 0xffffff) packed = concat([u8(0x63), uintToLEBytes(len, 3), b]);\n else packed = concat([u8(0x64), uintToLEBytes(len, 4), b]);\n } else if (data instanceof Uint8Array || Buffer.isBuffer(data)) {\n const bytes = data instanceof Uint8Array ? data : new Uint8Array(data);\n const len = bytes.length;\n if (len <= 0x20) packed = concat([u8(0x70 + len), bytes]);\n else if (len <= 0xff) packed = concat([u8(0x91), uintToLEBytes(len, 1), bytes]);\n else if (len <= 0xffff) packed = concat([u8(0x92), uintToLEBytes(len, 2), bytes]);\n else packed = concat([u8(0x93), uintToLEBytes(len, 4), bytes]);\n } else if (Array.isArray(data)) {\n const body = concat(data.map(d => _pack(d, objectList)));\n const len = data.length;\n if (len <= 0x0f) {\n packed = concat([u8(0xd0 + len), body]);\n if (len >= 0x0f) packed = concat([packed, u8(0x03)]);\n } else packed = concat([u8(0xdf), body, u8(0x03)]);\n } else if (typeof data === 'object') {\n const keys = Object.keys(data);\n const len = keys.length;\n const pairs: Uint8Array[] = [];\n for (const k of keys) {\n pairs.push(_pack(k, objectList));\n pairs.push(_pack((data as any)[k], objectList));\n }\n let header: Uint8Array;\n if (len <= 0x0f) {\n header = u8(0xE0 + len);\n } else {\n header = u8(0xEF);\n }\n packed = concatUint8Arrays([header, concatUint8Arrays(pairs)]);\n // terminator\n if (len >= 0x0f || objectList.some(v => v === packed)) {\n packed = concatUint8Arrays([packed, u8(0x81)]);\n }\n } else throw new TypeError(typeof data + '');\n\n // Object reuse\n const idx = objectList.findIndex(v => v.length === packed!.length && v.every((x, i) => x === packed![i]));\n if (idx >= 0) {\n if (idx < 0x21) packed = u8(0xA0 + idx);\n else if (idx <= 0xff) packed = concat([u8(0xC1), uintToLEBytes(idx, 1)]);\n else if (idx <= 0xffff) packed = concat([u8(0xC2), uintToLEBytes(idx, 2)]);\n else if (idx <= 0xffffffff) packed = concat([u8(0xC3), uintToLEBytes(idx, 4)]);\n else packed = concat([u8(0xC4), uintToLEBytes(idx, 8)]);\n } else if (packed!.length > 1) objectList.push(packed!);\n\n return packed!;\n}\n\n/* UNPACK */\nexport function unpack(data: Uint8Array): [any, Uint8Array] {\n return _unpack(data, []);\n}\n\nfunction ensureAvailable(buf: Uint8Array, need: number) {\n if (buf.length < need) throw new TypeError(`Not enough data: need ${need} bytes, have ${buf.length}`);\n}\n\nfunction readLittleEndian(buf: Uint8Array, offset: number, len: number) {\n ensureAvailable(buf.subarray(offset), len);\n let v = 0n;\n for (let i = len - 1; i >= 0; i--) v = (v << 8n) | BigInt(buf[offset + i]);\n return Number(v);\n}\n\nfunction _unpack(data: Uint8Array, objectList: any[]): [any, Uint8Array] {\n if (data.length === 0) throw new TypeError('No data to unpack');\n const tag = data[0];\n let addToObjectList = true;\n let value: any;\n let rest: Uint8Array;\n\n // simple tokens\n if (tag === 0x01) { value = true; rest = data.subarray(1); }\n else if (tag === 0x02) { value = false; rest = data.subarray(1); }\n else if (tag === 0x04) { value = null; rest = data.subarray(1); }\n else if (tag === 0x05) {\n value = data.subarray(1, 17);\n rest = data.subarray(17);\n }\n else if (tag === 0x06) {\n value = readLittleEndian(data, 1, 8);\n rest = data.subarray(9);\n }\n else if (tag >= 0x08 && tag <= 0x2f) {\n value = tag - 8;\n rest = data.subarray(1);\n }\n else if (tag === 0x35) {\n const view = new DataView(data.buffer, data.byteOffset + 1, 4);\n value = view.getFloat32(0, true);\n rest = data.subarray(5);\n }\n else if (tag === 0x36) {\n const view = new DataView(data.buffer, data.byteOffset + 1, 8);\n value = view.getFloat64(0, true);\n rest = data.subarray(9);\n }\n else if ((tag & 0xF0) === 0x30) {\n const noOfBytes = 2 ** (tag & 0xF);\n const val = readLittleEndian(data, 1, noOfBytes);\n value = sizedInt(val, noOfBytes);\n rest = data.subarray(1 + noOfBytes);\n }\n else if (tag >= 0x40 && tag <= 0x60) {\n const length = tag - 0x40;\n value = new TextDecoder().decode(data.subarray(1, 1 + length));\n rest = data.subarray(1 + length);\n }\n else if (tag >= 0x61 && tag <= 0x64) {\n const lenBytes = tag & 0xF;\n const length = readLittleEndian(data, 1, lenBytes);\n value = new TextDecoder().decode(data.subarray(1 + lenBytes, 1 + lenBytes + length));\n rest = data.subarray(1 + lenBytes + length);\n }\n else if (tag >= 0x70 && tag <= 0x90) {\n const length = tag - 0x70;\n value = data.subarray(1, 1 + length);\n rest = data.subarray(1 + length);\n }\n else if (tag >= 0x91 && tag <= 0x94) {\n const noOfBytes = 1 << ((tag & 0xF) - 1);\n const length = readLittleEndian(data, 1, noOfBytes);\n const start = 1 + noOfBytes;\n value = data.subarray(start, start + length);\n rest = data.subarray(start + length);\n }\n else if ((tag & 0xF0) === 0xD0) {\n const count = tag & 0xF;\n let ptr = data.subarray(1);\n const arr: any[] = [];\n if (count === 0xF) {\n while (ptr[0] !== 0x03) {\n const [v, r] = _unpack(ptr, objectList);\n arr.push(v);\n ptr = r;\n }\n ptr = ptr.subarray(1);\n } else {\n for (let i = 0; i < count; i++) {\n const [v, r] = _unpack(ptr, objectList);\n arr.push(v);\n ptr = r;\n }\n }\n value = arr;\n rest = ptr;\n addToObjectList = false;\n }\n else if ((tag & 0xE0) === 0xE0) {\n const count = tag & 0xF;\n let ptr = data.subarray(1);\n const obj: Record<string, any> = {};\n if (count === 0xF) {\n while (ptr[0] !== 0x03) {\n const [k, r1] = _unpack(ptr, objectList);\n const [v, r2] = _unpack(r1, objectList);\n obj[k] = v;\n ptr = r2;\n }\n ptr = ptr.subarray(1);\n } else {\n for (let i = 0; i < count; i++) {\n const [k, r1] = _unpack(ptr, objectList);\n const [v, r2] = _unpack(r1, objectList);\n obj[k] = v;\n ptr = r2;\n }\n }\n value = obj;\n rest = ptr;\n addToObjectList = false;\n }\n else if (tag >= 0xA0 && tag <= 0xC0) {\n const idx = tag - 0xA0;\n if (idx >= objectList.length) throw new TypeError(`Reference index ${idx} out of range`);\n value = objectList[idx];\n rest = data.subarray(1);\n addToObjectList = false;\n }\n else if (tag >= 0xC1 && tag <= 0xC4) {\n const len = tag - 0xC0;\n const uid = readLittleEndian(data, 1, len);\n if (uid >= objectList.length) throw new TypeError(`UID ${uid} out of range`);\n value = objectList[uid];\n rest = data.subarray(1 + len);\n addToObjectList = false;\n }\n else {\n throw new TypeError(`Unknown tag 0x${tag.toString(16)}`);\n }\n\n if (addToObjectList) objectList.push(value);\n return [value, rest];\n}\n",
9
+ "export { parse } from '@plist/binary.parse';\nexport { serialize } from '@plist/binary.serialize';\n",
10
+ "export const Flags = {\n TransientPairing: 0x10\n} as const;\n\nexport const Method = {\n PairSetup: 0x00,\n PairSetupWithAuth: 0x01,\n PairVerify: 0x02,\n AddPairing: 0x03,\n RemovePairing: 0x04,\n ListPairing: 0x05\n} as const;\n\nexport const State = {\n M1: 0x01,\n M2: 0x02,\n M3: 0x03,\n M4: 0x04,\n M5: 0x05,\n M6: 0x06\n} as const;\n\nexport const Value = {\n Method: 0x00,\n Identifier: 0x01,\n Salt: 0x02,\n PublicKey: 0x03,\n Proof: 0x04,\n EncryptedData: 0x05,\n State: 0x06,\n Error: 0x07,\n BackOff: 0x08,\n Certificate: 0x09,\n Signature: 0x0A,\n Permissions: 0x0B,\n FragmentData: 0x0C,\n FragmentLast: 0x0D,\n\n Name: 0x11,\n Flags: 0x13\n} as const;\n\nexport function bail(data: Map<number, Buffer>): never {\n if (data.has(Value.BackOff)) {\n const buffer = data.get(Value.BackOff);\n const time = buffer.readUintLE(0, buffer.length);\n\n throw new Error(`Device is busy, try again in ${time} seconds.`);\n }\n\n if (data.has(Value.Error)) {\n throw new Error(`Device returned an error code: ${data.get(Value.Error).readUint8()}`);\n }\n\n console.error(data);\n\n throw new Error('Invalid response');\n}\n\nexport function encode(entries: [number, number | Buffer][]): Buffer {\n const chunks: number[] = [];\n\n for (const [type, valueRaw] of entries) {\n let value: Buffer;\n\n if (typeof valueRaw === 'number') {\n value = Buffer.from([valueRaw]);\n } else {\n value = valueRaw;\n }\n\n let offset = 0;\n\n do {\n const len = Math.min(value.length - offset, 255);\n chunks.push(type, len);\n\n if (len > 0) {\n for (let i = 0; i < len; i++) {\n chunks.push(value[offset + i]);\n }\n }\n\n offset += len;\n } while (offset < value.length);\n }\n\n return Buffer.from(chunks);\n}\n\nexport function decode(buf: Buffer): Map<number, Buffer> {\n const map = new Map<number, Buffer>();\n let i = 0;\n\n while (i < buf.length) {\n const type = buf[i++];\n const len = buf[i++];\n\n const value = (new Uint8Array(buf)).slice(i, i + len);\n i += len;\n\n const existing = map.get(type);\n if (existing) {\n map.set(type, Buffer.concat([existing, value]));\n } else {\n map.set(type, Buffer.from(value));\n }\n }\n\n return map;\n}\n",
11
+ "import { randomInt } from 'node:crypto';\nimport { Socket } from 'node:net';\nimport { debug } from '@/cli';\nimport { decryptChacha20, encryptChacha20 } from '@/crypto';\nimport { decodeOPack, encodeOPack, opackSizedInt } from '@/encoding';\nimport BaseSocket from './base';\n\nconst HEADER_BYTES = 4;\n\nexport default class extends BaseSocket {\n get isEncrypted(): boolean {\n return !!this.#readKey && !!this.#writeKey;\n }\n\n readonly #socket: Socket;\n readonly #queue: Record<number, Function> = {};\n #buffer: Buffer = Buffer.alloc(0);\n #readCount: number;\n #readKey?: Buffer;\n #writeCount: number;\n #writeKey?: Buffer;\n #xid: number;\n\n constructor(address: string, port: number) {\n super(address, port);\n\n this.#xid = randomInt(0, 2 ** 16);\n\n this.onClose = this.onClose.bind(this);\n this.onConnect = this.onConnect.bind(this);\n this.onData = this.onData.bind(this);\n this.onEnd = this.onEnd.bind(this);\n this.onError = this.onError.bind(this);\n\n this.#socket = new Socket();\n this.#socket.on('close', this.onClose);\n this.#socket.on('connect', this.onConnect);\n this.#socket.on('data', this.onData);\n this.#socket.on('end', this.onEnd);\n this.#socket.on('error', this.onError);\n }\n\n async connect(): Promise<void> {\n debug(`Connecting to ${this.address}:${this.port}...`);\n\n return await new Promise(resolve => {\n this.#socket.connect({\n host: this.address,\n port: this.port,\n keepAlive: true\n }, resolve);\n });\n }\n\n async disconnect(): Promise<void> {\n this.#socket.destroy();\n }\n\n async enableEncryption(readKey: Buffer, writeKey: Buffer): Promise<void> {\n this.#readKey = readKey;\n this.#writeKey = writeKey;\n this.#readCount = 0;\n this.#writeCount = 0;\n }\n\n async exchange(type: number, obj: Record<string, unknown>): Promise<[number, unknown]> {\n const _x = this.#xid;\n\n return new Promise<[number, number]>((resolve, reject) => {\n if (PairFrameTypes.includes(type)) {\n this.#queue[-1] = resolve;\n } else {\n this.#queue[_x] = resolve;\n }\n\n this.send(type, obj).catch(reject);\n });\n }\n\n async send(type: number, obj: Record<string, unknown>): Promise<void> {\n const _x = this.#xid++;\n obj._x ??= opackSizedInt(_x, 8);\n\n let payload = Buffer.from(encodeOPack(obj));\n let payloadLength = payload.byteLength;\n\n if (this.isEncrypted && payloadLength > 0) {\n payloadLength += 16;\n }\n\n const header = Buffer.alloc(4);\n header.writeUint8(type, 0);\n header.writeUintBE(payloadLength, 1, 3);\n\n let data: Buffer;\n\n if (this.isEncrypted) {\n const nonce = Buffer.alloc(12);\n nonce.writeBigUInt64LE(BigInt(this.#writeCount++), 0);\n\n const encrypted = encryptChacha20(this.#writeKey, nonce, header, payload);\n data = Buffer.concat([header, encrypted.ciphertext, encrypted.authTag]);\n } else {\n data = Buffer.concat([header, payload]);\n }\n\n debug('Send data frame', this.isEncrypted, Buffer.from(data).toString('hex'), obj);\n\n return new Promise((resolve, reject) => {\n this.#socket.write(data, err => err && reject(err));\n resolve();\n });\n }\n\n async onClose(): Promise<void> {\n debug(`Connection closed from ${this.address}:${this.port}`);\n }\n\n async onConnect(): Promise<void> {\n debug(`Connected to ${this.address}:${this.port}`);\n }\n\n async onData(buffer: Buffer): Promise<void> {\n // debug('Received data frame', buffer.toString('hex'));\n\n this.#buffer = Buffer.concat([this.#buffer, buffer]);\n\n while (this.#buffer.byteLength >= HEADER_BYTES) {\n const header = this.#buffer.subarray(0, HEADER_BYTES);\n const payloadLength = header.readUintBE(1, 3);\n const totalLength = HEADER_BYTES + payloadLength;\n\n if (this.#buffer.byteLength < totalLength) {\n debug(`Not enough data yet, waiting on the next frame.. needed=${totalLength} available=${this.#buffer.byteLength} receivedLength=${buffer.byteLength}`);\n return;\n }\n\n debug(`Frame found length=${totalLength} availableLength=${this.#buffer.byteLength} receivedLength=${buffer.byteLength}`);\n\n const frame = Buffer.from(this.#buffer.subarray(0, totalLength));\n this.#buffer = this.#buffer.subarray(totalLength);\n\n debug(`Handle frame, ${this.#buffer.byteLength} bytes left...`);\n\n const data = await this.#decrypt(frame);\n let payload = data.subarray(4, totalLength);\n\n await this.#handle(header, payload);\n }\n }\n\n async onEnd(): Promise<void> {\n debug('Connection ended');\n }\n\n async onError(err: Error): Promise<void> {\n debug('Error received', err);\n }\n\n async #decrypt(data: Buffer): Promise<Buffer> {\n if (!this.isEncrypted) {\n return data;\n }\n\n const header = data.subarray(0, 4);\n const payloadLength = header.readUintBE(1, 3);\n\n const payload = data.subarray(4, 4 + payloadLength);\n const authTag = payload.subarray(payload.byteLength - 16);\n const ciphertext = payload.subarray(0, payload.byteLength - 16);\n\n const nonce = Buffer.alloc(12);\n nonce.writeBigUint64LE(BigInt(this.#readCount++), 0);\n\n const decrypted = decryptChacha20(this.#readKey, nonce, header, ciphertext, authTag);\n\n return Buffer.concat([header, decrypted, authTag]);\n }\n\n async #handle(header: Buffer, payload: Buffer): Promise<void> {\n const type = header.readInt8();\n\n if (!OPackFrameTypes.includes(type)) {\n debug('Packet not handled, no opack frame.');\n }\n\n [payload] = decodeOPack(payload);\n\n debug('Decoded OPACK', {header, payload});\n\n if ('_x' in payload) {\n const _x = (payload as any)._x;\n\n if (_x in this.#queue) {\n const resolve = this.#queue[_x] ?? null;\n resolve?.([header, payload]);\n\n delete this.#queue[_x];\n } else if ('_i' in payload) {\n this.dispatchEvent(new CustomEvent(payload['_i'] as string, {detail: payload['_c']}));\n } else {\n // probably an event\n const content = payload['_c'];\n const keys = Object.keys(content).map(k => k.substring(0, -3));\n\n for (const key of keys) {\n this.dispatchEvent(new CustomEvent(key, {detail: content[key]}));\n }\n }\n } else if (this.#queue[-1]) {\n const _x = -1;\n const resolve = this.#queue[_x] ?? null;\n resolve?.([header, payload]);\n\n delete this.#queue[_x];\n } else {\n debug('No handler for message', [header, payload]);\n }\n }\n}\n\nexport const FrameType = {\n Unknown: 0,\n Noop: 1,\n\n PS_Start: 3,\n PS_Next: 4,\n PV_Start: 5,\n PV_Next: 6,\n\n U_OPACK: 7,\n E_OPACK: 8,\n P_OPACK: 9,\n\n PA_Request: 10,\n PA_Response: 11,\n\n SessionStartRequest: 16,\n SessionStartResponse: 17,\n SessionData: 18,\n\n FamilyIdentityRequest: 32,\n FamilyIdentityResponse: 33,\n FamilyIdentityUpdate: 34\n} as const;\n\nexport const MessageType = {\n Event: 1,\n Request: 2,\n Response: 3\n} as const;\n\nexport const OPackFrameTypes: number[] = [\n FrameType.PS_Start,\n FrameType.PS_Next,\n FrameType.PV_Start,\n FrameType.PV_Next,\n\n FrameType.U_OPACK,\n FrameType.E_OPACK,\n FrameType.P_OPACK\n];\n\nconst PairFrameTypes: number[] = [\n FrameType.PS_Start,\n FrameType.PS_Next,\n FrameType.PV_Start,\n FrameType.PV_Next\n];\n",
12
+ "import { createCipher, createDecipher } from 'chacha';\n\nconst AUTH_TAG_LENGTH = 16;\nconst NONCE_LENGTH = 12;\n\nexport function decrypt(key: Buffer, nonce: Buffer, add: Buffer | null, ciphertext: Buffer, authTag: Buffer): Buffer {\n nonce = padNonce(nonce);\n\n const decipher = createDecipher(key, nonce);\n add && decipher.setAAD(add);\n decipher.setAuthTag(authTag);\n\n const plaintext = decipher._update(ciphertext);\n decipher._final();\n\n return plaintext;\n}\n\nexport function encrypt(key: Buffer, nonce: Buffer, aad: Buffer | null, plaintext: Buffer): EncryptedData {\n nonce = padNonce(nonce);\n\n const cipher = createCipher(key, nonce);\n aad && cipher.setAAD(aad);\n\n const ciphertext = cipher._update(plaintext);\n cipher._final();\n\n const authTag = cipher.getAuthTag();\n\n return {\n ciphertext: ciphertext,\n authTag: authTag\n };\n}\n\nexport function padNonce(nonce: Buffer): Buffer {\n if (nonce.length >= NONCE_LENGTH) {\n return nonce;\n }\n\n return Buffer.concat([\n Buffer.alloc(NONCE_LENGTH - nonce.length, 0),\n nonce\n ]);\n}\n\n// NOTE\n// Uncomment when Bun supports chacha20-poly1305 out of box.\n//\n// import { createCipheriv, createDecipheriv } from 'node:crypto';\n//\n// export function decrypt(key: Buffer, nonce: Buffer, aad: Buffer | null, ciphertext: Buffer, authTag: Buffer): Buffer {\n// if (nonce.length < NONCE_LENGTH) {\n// nonce = Buffer.concat([\n// Buffer.alloc(NONCE_LENGTH - nonce.length, 0),\n// nonce\n// ]);\n// }\n//\n// const decipher = createDecipheriv('chacha20-poly1305', key, nonce, {authTagLength: AUTH_TAG_LENGTH});\n// aad && decipher.setAAD(aad, {plaintextLength: ciphertext.length});\n// decipher.setAuthTag(authTag);\n//\n// const plaintext = decipher.update(ciphertext);\n// decipher.final();\n//\n// return plaintext;\n// }\n//\n// export function encrypt(key: Buffer, nonce: Buffer, aad: Buffer | null, plaintext: Buffer): EncryptedData {\n// if (nonce.length < NONCE_LENGTH) {\n// nonce = Buffer.concat([\n// Buffer.alloc(NONCE_LENGTH - nonce.length, 0),\n// nonce\n// ]);\n// }\n//\n// const cipher = createCipheriv('chacha20-poly1305', key, nonce, {authTagLength: AUTH_TAG_LENGTH});\n// aad && cipher.setAAD(aad, {plaintextLength: plaintext.length});\n//\n// const ciphertext = cipher.update(plaintext);\n// cipher.final();\n//\n// const authTag = cipher.getAuthTag();\n//\n// return {\n// ciphertext: ciphertext,\n// authTag: authTag\n// };\n// }\n\nexport type EncryptedData = {\n readonly ciphertext: Buffer;\n readonly authTag: Buffer;\n};\n",
13
+ "import { randomBytes } from 'node:crypto';\nimport { x25519 } from '@noble/curves/ed25519.js';\n\nexport function generateKeyPair(): KeyPair {\n const secretKey = randomBytes(32);\n const publicKey = x25519.getPublicKey(secretKey);\n\n return {\n publicKey,\n secretKey\n };\n}\n\nexport function generateSharedSecKey(priKey: Uint8Array, pubKey: Uint8Array): Uint8Array {\n return x25519.getSharedSecret(priKey, pubKey);\n}\n\ninterface KeyPair {\n readonly publicKey: Uint8Array;\n readonly secretKey: Uint8Array;\n}\n",
14
+ "import { hkdfSync } from 'node:crypto';\n\nexport default function (options: HKDFOptions): Buffer {\n return Buffer.from(hkdfSync(options.hash, options.key, options.salt, options.info, options.length));\n}\n\nexport type HKDFOptions = {\n readonly hash: string;\n readonly key: Buffer;\n readonly length: number;\n readonly salt: Buffer;\n readonly info: Buffer;\n};\n",
15
+ "export default class BaseSocket extends EventTarget {\n get address(): string {\n return this.#address;\n }\n\n get port(): number {\n return this.#port;\n }\n\n readonly #address: string;\n readonly #port: number;\n\n constructor(address: string, port: number) {\n super();\n this.#address = address;\n this.#port = port;\n }\n\n async connect(): Promise<void> {\n }\n}\n",
16
+ "import { randomInt } from 'node:crypto';\nimport { debug, waitFor } from '@/cli';\nimport { opackFloat, parseBinaryPlist } from '@/encoding';\nimport { CompanionLinkFrameType, CompanionLinkMessageType, type CompanionLinkSocket } from '@/socket';\nimport type CompanionLink from '../companionLink';\n\nexport default class Api {\n readonly #protocol: CompanionLink;\n readonly #socket: CompanionLinkSocket;\n\n constructor(protocol: CompanionLink, socket: CompanionLinkSocket) {\n this.#protocol = protocol;\n this.#socket = socket;\n }\n\n async fetchMediaControlStatus(): Promise<void> {\n const [, payload] = await this.#socket.exchange(CompanionLinkFrameType.E_OPACK, {\n _i: 'FetchMediaControlStatus',\n _t: CompanionLinkMessageType.Request,\n _c: {}\n });\n }\n\n async fetchNowPlayingInfo(): Promise<void> {\n const [, payload] = await this.#socket.exchange(CompanionLinkFrameType.E_OPACK, {\n _i: 'FetchCurrentNowPlayingInfoEvent',\n _t: CompanionLinkMessageType.Request,\n _c: {}\n });\n }\n\n async fetchSupportedActions(): Promise<void> {\n const [, payload] = await this.#socket.exchange(CompanionLinkFrameType.E_OPACK, {\n _i: 'FetchSupportedActionsEvent',\n _t: CompanionLinkMessageType.Request,\n _c: {}\n });\n }\n\n async getAttentionState(): Promise<AttentionState> {\n const [, payload] = await this.#socket.exchange(CompanionLinkFrameType.E_OPACK, {\n _i: 'FetchAttentionState',\n _t: CompanionLinkMessageType.Request,\n _c: {}\n });\n\n const {_c} = objectOrFail<AttentionStateResponse>(payload);\n\n switch (_c.state) {\n case 0x01:\n return 'asleep';\n\n case 0x02:\n return 'screensaver';\n\n case 0x03:\n return 'awake';\n\n case 0x04:\n return 'idle';\n\n default:\n return 'unknown';\n }\n }\n\n async getLaunchableApps(): Promise<LaunchableApp[]> {\n const [, payload] = await this.#socket.exchange(CompanionLinkFrameType.E_OPACK, {\n _i: 'FetchLaunchableApplicationssEvent',\n _t: CompanionLinkMessageType.Request,\n _c: {}\n });\n\n const {_c} = objectOrFail<LaunchableAppsResponse>(payload);\n\n return Object.entries(_c).map(([bundleId, name]) => ({\n bundleId,\n name\n }));\n }\n\n async getSiriRemoteInfo(): Promise<any> {\n const [, payload] = await this.#socket.exchange(CompanionLinkFrameType.E_OPACK, {\n _i: 'FetchSiriRemoteInfo',\n _t: CompanionLinkMessageType.Request,\n _c: {}\n });\n\n return parseBinaryPlist(Buffer.from(payload['_c']['SiriRemoteInfoKey']).buffer);\n }\n\n async getUserAccounts(): Promise<UserAccount[]> {\n const [, payload] = await this.#socket.exchange(CompanionLinkFrameType.E_OPACK, {\n _i: 'FetchUserAccountsEvent',\n _t: CompanionLinkMessageType.Request,\n _c: {}\n });\n\n const {_c} = objectOrFail<UserAccountsResponse>(payload);\n\n return Object.entries(_c).map(([accountId, name]) => ({\n accountId,\n name\n }));\n }\n\n async hidCommand(command: keyof typeof HidCommand, down = false): Promise<void> {\n await this.#socket.exchange(CompanionLinkFrameType.E_OPACK, {\n _i: '_hidC',\n _t: CompanionLinkMessageType.Request,\n _c: {\n _hBtS: down ? 1 : 2,\n _hidC: HidCommand[command]\n }\n });\n }\n\n async launchApp(bundleId: string): Promise<void> {\n await this.#socket.exchange(CompanionLinkFrameType.E_OPACK, {\n _i: '_launchApp',\n _t: CompanionLinkMessageType.Request,\n _c: {\n _bundleID: bundleId\n }\n });\n }\n\n async launchUrl(url: string): Promise<void> {\n await this.#socket.exchange(CompanionLinkFrameType.E_OPACK, {\n _i: '_launchApp',\n _t: CompanionLinkMessageType.Request,\n _c: {\n _urlS: url\n }\n });\n }\n\n async mediaControlCommand(command: keyof typeof MediaControlCommand, content?: object): Promise<object> {\n const [, payload] = await this.#socket.exchange(CompanionLinkFrameType.E_OPACK, {\n _i: '_mcc',\n _t: CompanionLinkMessageType.Request,\n _c: {\n _mcc: MediaControlCommand[command],\n ...(content || {})\n }\n });\n\n return objectOrFail(payload);\n }\n\n async pressButton(command: keyof typeof HidCommand, type: ButtonPressType = 'SingleTap', holdDelayMs = 500): Promise<void> {\n switch (type) {\n case 'DoubleTap':\n await this.hidCommand(command, true);\n await this.hidCommand(command, false);\n\n await this.hidCommand(command, true);\n await this.hidCommand(command, false);\n break;\n\n case 'Hold':\n await this.hidCommand(command, true);\n await waitFor(holdDelayMs);\n await this.hidCommand(command, false);\n break;\n\n case 'SingleTap':\n await this.hidCommand(command, true);\n await this.hidCommand(command, false);\n break;\n }\n }\n\n async switchUserAccount(accountId: string): Promise<void> {\n await this.#socket.exchange(CompanionLinkFrameType.E_OPACK, {\n _i: 'SwitchUserAccountEvent',\n _t: CompanionLinkMessageType.Request,\n _c: {\n SwitchAccountID: accountId\n }\n });\n }\n\n async _subscribe(event: string, fn: EventListener): Promise<void> {\n this.#socket.addEventListener(event, fn);\n\n await this.#socket.send(CompanionLinkFrameType.E_OPACK, {\n _i: '_interest',\n _t: CompanionLinkMessageType.Event,\n _c: {\n _regEvents: [event]\n }\n });\n }\n\n async _unsubscribe(event: string, fn?: EventListener): Promise<void> {\n if (fn) {\n this.#socket.removeEventListener(event, fn);\n }\n\n await this.#socket.send(CompanionLinkFrameType.E_OPACK, {\n _i: '_interest',\n _t: CompanionLinkMessageType.Event,\n _c: {\n _deregEvents: [event]\n }\n });\n }\n\n async _sessionStart(): Promise<object> {\n const [, payload] = await this.#socket.exchange(CompanionLinkFrameType.E_OPACK, {\n _i: '_sessionStart',\n _t: CompanionLinkMessageType.Request,\n _c: {\n _srvT: 'com.apple.tvremoteservices',\n _sid: randomInt(0, 2 ** 32 - 1)\n }\n });\n\n return objectOrFail(payload);\n }\n\n async _systemInfo(pairingId: Buffer): Promise<object> {\n const [, payload] = await this.#socket.exchange(CompanionLinkFrameType.E_OPACK, {\n _i: '_systemInfo',\n _t: CompanionLinkMessageType.Request,\n _c: {\n _bf: 0,\n _cf: 512,\n _clFl: 128,\n _i: 'cafecafecafe',\n _idsID: pairingId.toString(),\n _pubID: 'FF:70:79:61:74:76',\n _sf: 256,\n _sv: '170.18',\n model: 'iPhone10,6',\n name: 'Bas Companion Link'\n }\n });\n\n return objectOrFail(payload);\n }\n\n async _touchStart(): Promise<object> {\n const [, payload] = await this.#socket.exchange(CompanionLinkFrameType.E_OPACK, {\n _i: '_touchStart',\n _t: CompanionLinkMessageType.Request,\n _c: {\n _height: opackFloat(1000.0),\n _tFl: 0,\n _width: opackFloat(1000.0)\n }\n });\n\n return objectOrFail(payload);\n }\n\n async _tvrcSessionStart(): Promise<object> {\n const [, payload] = await this.#socket.exchange(CompanionLinkFrameType.E_OPACK, {\n _i: 'TVRCSessionStart',\n _t: CompanionLinkMessageType.Request,\n _btHP: false,\n _inUseProc: 'tvremoted',\n _c: {}\n });\n\n return objectOrFail(payload);\n }\n}\n\nconst HidCommand = {\n Up: 1,\n Down: 2,\n Left: 3,\n Right: 4,\n Menu: 5,\n Select: 6,\n Home: 7,\n VolumeUp: 8,\n VolumeDown: 9,\n Siri: 10,\n Screensaver: 11,\n Sleep: 12,\n Wake: 13,\n PlayPause: 14,\n ChannelIncrement: 15,\n ChannelDecrement: 16,\n Guide: 17,\n PageUp: 18,\n PageDown: 19\n} as const;\n\nconst MediaControlCommand = {\n Play: 1,\n Pause: 2,\n NextTrack: 3,\n PreviousTrack: 4,\n GetVolume: 5,\n SetVolume: 6,\n SkipBy: 7,\n FastForwardBegin: 8,\n FastForwardEnd: 9,\n RewindBegin: 10,\n RewindEnd: 11,\n GetCaptionSettings: 12,\n SetCaptionSettings: 13\n} as const;\n\ntype ButtonPressType =\n | 'DoubleTap'\n | 'Hold'\n | 'SingleTap';\n\nfunction objectOrFail<T = object>(obj: unknown): T {\n if (typeof obj === 'object') {\n return obj as T;\n }\n\n debug('Expected an object.', {obj});\n\n throw new Error('Expected an object.');\n}\n\ntype AttentionState =\n | 'unknown'\n | 'asleep'\n | 'screensaver'\n | 'awake'\n | 'idle';\n\ntype AttentionStateResponse = {\n readonly _c: {\n readonly state: number;\n };\n};\n\ntype LaunchableApp = {\n readonly bundleId: string;\n readonly name: string;\n};\n\ntype LaunchableAppsResponse = {\n readonly _c: Record<string, string>;\n};\n\ntype UserAccount = {\n readonly accountId: string;\n readonly name: string;\n};\n\ntype UserAccountsResponse = {\n readonly _c: Record<string, string>;\n};\n",
17
+ "import { CompanionLinkFrameType, type CompanionLinkSocket } from '@/socket';\nimport { SRP, SrpClient } from 'fast-srp-hap';\nimport { v4 as uuid } from 'uuid';\nimport tweetnacl from 'tweetnacl';\nimport { debug } from '@/cli';\nimport { AIRPLAY_TRANSIENT_PIN } from '@/const';\nimport { decryptChacha20, encryptChacha20, hkdf } from '@/crypto';\nimport { bailTlv, decodeTlv, encodeOPack, encodeTlv, TlvFlags, TlvMethod, TlvState, TlvValue } from '@/encoding';\nimport CompanionLink from '../companionLink';\n\nexport default class {\n get name(): string {\n return this.#name;\n }\n\n get pairingId(): Buffer {\n return this.#pairingId;\n }\n\n readonly #name: string;\n readonly #pairingId: Buffer;\n readonly #protocol: CompanionLink;\n readonly #socket: CompanionLinkSocket;\n #publicKey: Buffer;\n #secretKey: Buffer;\n #srp: SrpClient;\n\n constructor(protocol: CompanionLink, socket: CompanionLinkSocket) {\n this.#protocol = protocol;\n this.#socket = socket;\n\n this.#name = 'Bas Companion Link';\n this.#pairingId = Buffer.from(uuid().toUpperCase());\n }\n\n async start(): Promise<void> {\n const keyPair = tweetnacl.sign.keyPair();\n this.#publicKey = Buffer.from(keyPair.publicKey);\n this.#secretKey = Buffer.from(keyPair.secretKey);\n }\n\n async pin(askPin: () => Promise<string>): Promise<PairingCredentials> {\n const m1 = await this.m1();\n const m2 = await this.m2(m1, await askPin());\n const m3 = await this.m3(m2);\n const m4 = await this.m4(m3);\n const m5 = await this.m5(m4);\n const m6 = await this.m6(m4, m5);\n\n if (!m6) {\n throw new Error('Pairing failed, could not get accessory keys.');\n }\n\n return m6;\n }\n\n async transient(): Promise<TransientPairingCredentials> {\n const m1 = await this.m1([[TlvValue.Flags, TlvFlags.TransientPairing]]);\n const m2 = await this.m2(m1);\n const m3 = await this.m3(m2);\n const m4 = await this.m4(m3);\n\n const accessoryToControllerKey = hkdf({\n hash: 'sha512',\n key: m4.sharedSecret,\n length: 32,\n salt: Buffer.from('Control-Salt'),\n info: Buffer.from('Control-Read-Encryption-Key')\n });\n\n const controllerToAccessoryKey = hkdf({\n hash: 'sha512',\n key: m4.sharedSecret,\n length: 32,\n salt: Buffer.from('Control-Salt'),\n info: Buffer.from('Control-Write-Encryption-Key')\n });\n\n return {\n pairingId: this.#pairingId,\n sharedSecret: m4.sharedSecret,\n accessoryToControllerKey,\n controllerToAccessoryKey\n };\n }\n\n async m1(additionalTlv: [number, number | Buffer][] = []): Promise<M1> {\n const [, response] = await this.#socket.exchange(CompanionLinkFrameType.PS_Start, {\n _pd: encodeTlv([\n [TlvValue.Method, TlvMethod.PairSetup],\n [TlvValue.State, TlvState.M1],\n ...additionalTlv\n ]),\n _pwTy: 1\n });\n\n const data = this.#tlv(response);\n const publicKey = data.get(TlvValue.PublicKey);\n const salt = data.get(TlvValue.Salt);\n\n return {publicKey, salt};\n }\n\n async m2(m1: M1, pin: string = AIRPLAY_TRANSIENT_PIN): Promise<M2> {\n const srpKey = await SRP.genKey(32);\n\n this.#srp = new SrpClient(SRP.params.hap, m1.salt, Buffer.from('Pair-Setup'), Buffer.from(pin), srpKey, true);\n this.#srp.setB(m1.publicKey);\n\n const publicKey = this.#srp.computeA();\n const proof = this.#srp.computeM1();\n\n return {publicKey, proof};\n }\n\n async m3(m2: M2): Promise<M3> {\n const [, response] = await this.#socket.exchange(CompanionLinkFrameType.PS_Next, {\n _pd: encodeTlv([\n [TlvValue.State, TlvState.M3],\n [TlvValue.PublicKey, m2.publicKey],\n [TlvValue.Proof, m2.proof]\n ]),\n _pwTy: 1\n });\n\n const data = this.#tlv(response);\n const serverProof = data.get(TlvValue.Proof);\n\n return {serverProof};\n }\n\n async m4(m3: M3): Promise<M4> {\n this.#srp.checkM2(m3.serverProof);\n\n const sharedSecret = this.#srp.computeK();\n\n return {sharedSecret};\n }\n\n async m5(m4: M4): Promise<M5> {\n const iosDeviceX = hkdf({\n hash: 'sha512',\n key: m4.sharedSecret,\n length: 32,\n salt: Buffer.from('Pair-Setup-Controller-Sign-Salt', 'utf8'),\n info: Buffer.from('Pair-Setup-Controller-Sign-Info', 'utf8')\n });\n\n const sessionKey = hkdf({\n hash: 'sha512',\n key: m4.sharedSecret,\n length: 32,\n salt: Buffer.from('Pair-Setup-Encrypt-Salt', 'utf8'),\n info: Buffer.from('Pair-Setup-Encrypt-Info', 'utf8')\n });\n\n const deviceInfo = Buffer.concat([\n iosDeviceX,\n this.#pairingId,\n this.#publicKey\n ]);\n\n const signature = tweetnacl.sign.detached(deviceInfo, this.#secretKey);\n\n const innerTlv = encodeTlv([\n [TlvValue.Identifier, this.#pairingId],\n [TlvValue.PublicKey, this.#publicKey],\n [TlvValue.Signature, Buffer.from(signature)],\n [TlvValue.Name, Buffer.from(encodeOPack({\n name: this.#name\n }))]\n ]);\n\n const {authTag, ciphertext} = encryptChacha20(sessionKey, Buffer.from('PS-Msg05'), null, innerTlv);\n const encrypted = Buffer.concat([ciphertext, authTag]);\n\n const [, response] = await this.#socket.exchange(CompanionLinkFrameType.PS_Next, {\n _pd: encodeTlv([\n [TlvValue.State, TlvState.M5],\n [TlvValue.EncryptedData, encrypted]\n ]),\n _pwTy: 1\n });\n\n const data = this.#tlv(response);\n const encryptedDataRaw = data.get(TlvValue.EncryptedData);\n const encryptedData = encryptedDataRaw.subarray(0, -16);\n const encryptedTag = encryptedDataRaw.subarray(-16);\n\n return {\n authTag: encryptedTag,\n data: encryptedData,\n sessionKey\n };\n }\n\n async m6(m4: M4, m5: M5): Promise<PairingCredentials> {\n const data = decryptChacha20(m5.sessionKey, Buffer.from('PS-Msg06'), null, m5.data, m5.authTag);\n const tlv = decodeTlv(data);\n\n const accessoryIdentifier = tlv.get(TlvValue.Identifier);\n const accessoryLongTermPublicKey = tlv.get(TlvValue.PublicKey);\n const accessorySignature = tlv.get(TlvValue.Signature);\n\n const accessoryX = hkdf({\n hash: 'sha512',\n key: m4.sharedSecret,\n length: 32,\n salt: Buffer.from('Pair-Setup-Accessory-Sign-Salt'),\n info: Buffer.from('Pair-Setup-Accessory-Sign-Info')\n });\n\n const accessoryInfo = Buffer.concat([\n accessoryX,\n accessoryIdentifier,\n accessoryLongTermPublicKey\n ]);\n\n if (!tweetnacl.sign.detached.verify(accessoryInfo, accessorySignature, accessoryLongTermPublicKey)) {\n throw new Error('Invalid accessory signature.');\n }\n\n return {\n accessoryIdentifier: accessoryIdentifier.toString(),\n accessoryLongTermPublicKey: accessoryLongTermPublicKey,\n pairingId: this.#pairingId,\n publicKey: this.#publicKey,\n secretKey: this.#secretKey\n };\n }\n\n #tlv(response: unknown): Map<number, Buffer> {\n if (typeof response !== 'object' || response === null) {\n throw new Error('Invalid response from receiver.');\n }\n\n const data = decodeTlv(response['_pd']);\n\n if (data.has(TlvValue.Error)) {\n bailTlv(data);\n }\n\n debug('Decoded TLV', data);\n\n return data;\n }\n}\n\ntype M1 = {\n readonly publicKey: Buffer;\n readonly salt: Buffer;\n}\n\ntype M2 = {\n readonly publicKey: Buffer;\n readonly proof: Buffer;\n};\n\ntype M3 = {\n readonly serverProof: Buffer;\n};\n\ntype M4 = {\n readonly sharedSecret: Buffer;\n};\n\ntype M5 = {\n readonly authTag: Buffer;\n readonly data: Buffer;\n readonly sessionKey: Buffer;\n};\n\ntype PairingCredentials = {\n readonly accessoryIdentifier: string;\n readonly accessoryLongTermPublicKey: Buffer;\n readonly pairingId: Buffer;\n readonly publicKey: Buffer;\n readonly secretKey: Buffer;\n};\n\ntype TransientPairingCredentials = {\n readonly pairingId: Buffer;\n readonly sharedSecret: Buffer;\n readonly accessoryToControllerKey: Buffer;\n readonly controllerToAccessoryKey: Buffer;\n};\n",
18
+ "import tweetnacl, { type BoxKeyPair } from 'tweetnacl';\nimport { debug } from '@/cli';\nimport { decryptChacha20, encryptChacha20, generateCurve25519KeyPair, generateCurve25519SharedSecKey, hkdf } from '@/crypto';\nimport { bailTlv, decodeTlv, encodeTlv, TlvState, TlvValue } from '@/encoding';\nimport { CompanionLinkFrameType, CompanionLinkSocket } from '@/socket';\nimport CompanionLink from '../companionLink';\n\nexport default class {\n readonly #protocol: CompanionLink;\n readonly #socket: CompanionLinkSocket;\n readonly #ephemeralKeyPair: BoxKeyPair;\n\n constructor(protocol: CompanionLink, socket: CompanionLinkSocket) {\n this.#protocol = protocol;\n this.#socket = socket;\n this.#ephemeralKeyPair = generateCurve25519KeyPair();\n }\n\n async start(credentials: Credentials): Promise<AccessoryKeys> {\n const m1 = await this.#m1();\n const m2 = await this.#m2(credentials.accessoryIdentifier, credentials.accessoryLongTermPublicKey, m1);\n\n await this.#m3(credentials.pairingId, credentials.secretKey, m2);\n\n return await this.#m4(m2);\n }\n\n async #m1(): Promise<M1> {\n const [, response] = await this.#socket.exchange(CompanionLinkFrameType.PV_Start, {\n _pd: encodeTlv([\n [TlvValue.State, TlvState.M1],\n [TlvValue.PublicKey, Buffer.from(this.#ephemeralKeyPair.publicKey)]\n ]),\n _auTy: 4\n });\n\n const data = this.#tlv(response);\n const serverPublicKey = data.get(TlvValue.PublicKey);\n const encryptedData = data.get(TlvValue.EncryptedData);\n\n return {\n encryptedData,\n serverPublicKey\n };\n }\n\n async #m2(localAccessoryIdentifier: string, longTermPublicKey: Buffer, m1: M1): Promise<M2> {\n const sharedSecret = Buffer.from(generateCurve25519SharedSecKey(\n this.#ephemeralKeyPair.secretKey,\n m1.serverPublicKey\n ));\n\n const sessionKey = hkdf({\n hash: 'sha512',\n key: sharedSecret,\n length: 32,\n salt: Buffer.from('Pair-Verify-Encrypt-Salt'),\n info: Buffer.from('Pair-Verify-Encrypt-Info')\n });\n\n const encryptedData = m1.encryptedData.subarray(0, -16);\n const encryptedTag = m1.encryptedData.subarray(-16);\n\n const data = decryptChacha20(sessionKey, Buffer.from('PV-Msg02'), null, encryptedData, encryptedTag);\n const tlv = decodeTlv(data);\n\n const accessoryIdentifier = tlv.get(TlvValue.Identifier);\n const accessorySignature = tlv.get(TlvValue.Signature);\n\n if (accessoryIdentifier.toString() !== localAccessoryIdentifier) {\n throw new Error(`Invalid accessory identifier. Expected ${accessoryIdentifier.toString()} to be ${localAccessoryIdentifier}.`);\n }\n\n const accessoryInfo = Buffer.concat([\n m1.serverPublicKey,\n accessoryIdentifier,\n this.#ephemeralKeyPair.publicKey\n ]);\n\n if (!tweetnacl.sign.detached.verify(accessoryInfo, accessorySignature, longTermPublicKey)) {\n throw new Error('Invalid accessory signature.');\n }\n\n return {\n serverEphemeralPublicKey: m1.serverPublicKey,\n sessionKey,\n sharedSecret\n };\n }\n\n async #m3(pairingId: Buffer, secretKey: Buffer, m2: M2): Promise<M3> {\n const iosDeviceInfo = Buffer.concat([\n this.#ephemeralKeyPair.publicKey,\n pairingId,\n m2.serverEphemeralPublicKey\n ]);\n\n const iosDeviceSignature = Buffer.from(tweetnacl.sign.detached(iosDeviceInfo, secretKey));\n\n const innerTlv = encodeTlv([\n [TlvValue.Identifier, pairingId],\n [TlvValue.Signature, iosDeviceSignature]\n ]);\n\n const {authTag, ciphertext} = encryptChacha20(m2.sessionKey, Buffer.from('PV-Msg03'), null, innerTlv);\n const encrypted = Buffer.concat([ciphertext, authTag]);\n\n const [, response] = await this.#socket.exchange(CompanionLinkFrameType.PV_Next, {\n _pd: encodeTlv([\n [TlvValue.State, TlvState.M3],\n [TlvValue.EncryptedData, encrypted]\n ]),\n _auTy: 4\n });\n\n console.log(this.#tlv(response));\n\n return {};\n }\n\n async #m4(m2: M2): Promise<AccessoryKeys> {\n const accessoryToControllerKey = hkdf({\n hash: 'sha512',\n key: m2.sharedSecret,\n length: 32,\n salt: Buffer.alloc(0),\n info: Buffer.from('ServerEncrypt-main')\n });\n\n const controllerToAccessoryKey = hkdf({\n hash: 'sha512',\n key: m2.sharedSecret,\n length: 32,\n salt: Buffer.alloc(0),\n info: Buffer.from('ClientEncrypt-main')\n });\n\n return {\n accessoryToControllerKey,\n controllerToAccessoryKey\n };\n }\n\n #tlv(response: unknown): Map<number, Buffer> {\n if (typeof response !== 'object' || response === null) {\n throw new Error('Invalid response from receiver.');\n }\n\n const data = decodeTlv(response['_pd']);\n\n if (data.has(TlvValue.Error)) {\n bailTlv(data);\n }\n\n debug('Decoded TLV', data);\n\n return data;\n }\n}\n\ntype Credentials = {\n readonly accessoryIdentifier: string;\n readonly accessoryLongTermPublicKey: Buffer;\n readonly pairingId: Buffer;\n readonly publicKey: Buffer;\n readonly secretKey: Buffer;\n};\n\ntype M1 = {\n readonly encryptedData: Buffer;\n readonly serverPublicKey: Buffer;\n};\n\ntype M2 = {\n readonly serverEphemeralPublicKey: Buffer;\n readonly sessionKey: Buffer;\n readonly sharedSecret: Buffer;\n};\n\ntype M3 = {};\n\ntype AccessoryKeys = {\n readonly accessoryToControllerKey: Buffer;\n readonly controllerToAccessoryKey: Buffer;\n};\n",
19
+ "import { Result } from 'node-dns-sd';\nimport { CompanionLinkSocket } from '@/socket';\nimport Api from './api/companionLink';\nimport Pairing from './pairing/companionLink';\nimport Verify from './verify/companionLink';\n\nexport default class CompanionLink {\n get api(): Api {\n return this.#api;\n }\n\n get socket(): CompanionLinkSocket {\n return this.#socket;\n }\n\n get pairing(): Pairing {\n return this.#pairing;\n }\n\n get verify(): Verify {\n return this.#verify;\n }\n\n readonly #api: Api;\n readonly #device: Result;\n readonly #socket: CompanionLinkSocket;\n readonly #pairing: Pairing;\n readonly #verify: Verify;\n\n constructor(device: Result) {\n this.#device = device;\n this.#socket = new CompanionLinkSocket(device.address, device.service.port);\n this.#api = new Api(this, this.#socket);\n this.#pairing = new Pairing(this, this.#socket);\n this.#verify = new Verify(this, this.#socket);\n }\n\n async connect(): Promise<void> {\n await this.#socket.connect();\n }\n}\n",
20
+ "import { write } from 'bun';\nimport { debug, prompt, waitFor } from '@/cli';\nimport { Discovery } from '@/discovery';\nimport { parseBinaryPlist } from '@/encoding';\nimport { CompanionLink } from '@/protocol';\n\nconst discovery = Discovery.companionLink();\nconst device = await discovery.findUntil('Woonkamer TV._companion-link._tcp.local');\n\nconst protocol = new CompanionLink(device);\nawait protocol.connect();\n\nasync function pair(): Promise<void> {\n await protocol.pairing.start();\n const credentials = await protocol.pairing.pin(async () => await prompt('Enter PIN'));\n\n console.log({\n accessoryIdentifier: credentials.accessoryIdentifier,\n accessoryLongTermPublicKey: credentials.accessoryLongTermPublicKey.toString('hex'),\n pairingId: credentials.pairingId.toString('hex'),\n publicKey: credentials.publicKey.toString('hex'),\n secretKey: credentials.secretKey.toString('hex')\n });\n}\n\nasync function verify(): Promise<void> {\n const credentials = {\n accessoryIdentifier: '7EEEA518-06CC-486C-A8B8-4A07CDBE6267',\n accessoryLongTermPublicKey: Buffer.from('cfb3fb0e0eb494d9058d5051c94400b35251e3faad66542b9551a1496570628d', 'hex'),\n pairingId: Buffer.from('31343733314638332d334438422d344242362d393530302d433544464243383737443735', 'hex'),\n publicKey: Buffer.from('4b7707b544e9c3b7a3609d0c9f7c9f013a71b415568bff597013d65e14d88918', 'hex'),\n secretKey: Buffer.from('3933f2526014609fc437d2b0c7968b476f3ee0fd2d836fc5e97971fbd26adae04b7707b544e9c3b7a3609d0c9f7c9f013a71b415568bff597013d65e14d88918', 'hex')\n };\n\n const keys = await protocol.verify.start(credentials);\n\n await protocol.socket.enableEncryption(\n keys.accessoryToControllerKey,\n keys.controllerToAccessoryKey\n );\n\n await protocol.api._systemInfo(credentials.pairingId);\n await protocol.api._touchStart();\n await protocol.api._sessionStart();\n await protocol.api._tvrcSessionStart();\n\n await protocol.api._unsubscribe('_iMC');\n await protocol.api._subscribe('TVSystemStatus', (e: CustomEvent) => debug(e));\n\n // await protocol.api._subscribe('NowPlayingInfo', handleNowPlayingInfo);\n // await protocol.api.fetchNowPlayingInfo();\n\n // await protocol.api._subscribe('SupportedActions', (e: CustomEvent) => debug(e));\n // await protocol.api.fetchSupportedActions();\n\n // await protocol.api._subscribe('PushSiriRemoteInfo', (e: CustomEvent) => debug(e));\n // const data = await protocol.api.getSiriRemoteInfo();\n // debug(data);\n\n // await protocol.api._subscribe('MediaControlStatus', (e: CustomEvent) => debug(e));\n // const data = await protocol.api.fetchMediaControlStatus();\n // debug(data);\n\n // debug('Attention state', await protocol.api.getAttentionState());\n // debug('Launchable apps', await protocol.api.getLaunchableApps());\n // debug('Available user accounts', await protocol.api.getUserAccounts());\n\n // await protocol.api.launchApp('com.apple.TVMusic');\n // await protocol.api.launchUrl('nflx://www.netflix.com/title/70291117');\n // await protocol.api.switchUserAccount('71A6CA15-5268-4820-9DD8-1C53F980C149');\n\n // await protocol.api.pressButton('Select');\n // await protocol.api.pressButton('VolumeDown');\n\n // await protocol.api.mediaControlCommand('Pause');\n // await waitFor(2000);\n // await protocol.api.mediaControlCommand('Play');\n\n // await protocol.api.pressButton('PageUp');\n\n // await protocol.api.pressButton('Sleep');\n // await protocol.api.pressButton('Wake');\n\n // await protocol.api.mediaControlCommand('GetVolume');\n}\n\nasync function handleNowPlayingInfo(evt: CustomEvent): Promise<void> {\n const {detail: {NowPlayingInfoKey}} = evt;\n const buffer = NowPlayingInfoKey.buffer.slice(\n NowPlayingInfoKey.byteOffset,\n NowPlayingInfoKey.byteOffset + NowPlayingInfoKey.byteLength\n );\n\n try {\n const nowPlaying = parseBinaryPlist(buffer) as any;\n console.log('NowPlayingInfoKey', {nowPlaying});\n } catch (err) {\n console.error(err);\n // console.error(Buffer.from(buffer).toString());\n }\n\n // if (!nowPlaying.$objects[15]) {\n // try {\n // await write('./artwork.png', nowPlaying.$objects[14]);\n // } catch (_) {\n // }\n // }\n //\n // if (nowPlaying.$objects[4]) {\n // debug(`Now playing ${nowPlaying.$objects[8]} on Apple TV.`);\n // } else {\n // debug('Not playing?');\n // }\n //\n // // debug(nowPlaying);\n // // debug('Keys', nowPlaying.$objects[1]);\n // // debug('Image data is placeholder', nowPlaying.$objects[15]);\n // // debug('metadata', nowPlaying.$objects[6]);\n // debug('playback state', nowPlaying.$objects[4]);\n}\n\n// await pair();\nawait verify();\n"
21
+ ],
22
+ "mappings": "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",
23
+ "debugId": "81BC0BEED4FB133964756E2164756E21",
24
+ "names": []
25
+ }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@basmilius/apple-companion-link",
3
3
  "description": "Implementation of Apple's Companion Link",
4
- "version": "0.0.2",
4
+ "version": "0.0.4",
5
5
  "type": "module",
6
6
  "license": "MIT",
7
7
  "author": {
@@ -18,7 +18,6 @@
18
18
  ],
19
19
  "files": [
20
20
  "dist",
21
- "src",
22
21
  "CODEOWNERS",
23
22
  "LICENSE"
24
23
  ],
package/src/cli.ts DELETED
@@ -1,19 +0,0 @@
1
- import { createInterface } from 'node:readline';
2
- import { styleText } from 'node:util';
3
-
4
- const stdin = createInterface({
5
- input: process.stdin,
6
- output: process.stdout
7
- });
8
-
9
- export function debug(...data: any[]): void {
10
- console.debug(styleText('cyan', '[debug]'), ...data);
11
- }
12
-
13
- export async function prompt(message: string): Promise<string> {
14
- return await new Promise<string>(resolve => stdin.question(`${message}: `, resolve));
15
- }
16
-
17
- export async function waitFor(ms: number): Promise<void> {
18
- return new Promise(resolve => setTimeout(resolve, ms));
19
- }
package/src/const.ts DELETED
@@ -1,7 +0,0 @@
1
- export const AIRPLAY_TRANSIENT_PIN = '3939';
2
-
3
- export const HTTP_TIMEOUT = 15000;
4
-
5
- export const AIRPLAY_SERVICE = '_airplay._tcp.local';
6
- export const COMPANION_LINK_SERVICE = '_companion-link._tcp.local';
7
- export const RAOP_SERVICE = '_raop._tcp.local';
@@ -1,95 +0,0 @@
1
- import { createCipher, createDecipher } from 'chacha';
2
-
3
- const AUTH_TAG_LENGTH = 16;
4
- const NONCE_LENGTH = 12;
5
-
6
- export function decrypt(key: Buffer, nonce: Buffer, add: Buffer | null, ciphertext: Buffer, authTag: Buffer): Buffer {
7
- nonce = padNonce(nonce);
8
-
9
- const decipher = createDecipher(key, nonce);
10
- add && decipher.setAAD(add);
11
- decipher.setAuthTag(authTag);
12
-
13
- const plaintext = decipher._update(ciphertext);
14
- decipher._final();
15
-
16
- return plaintext;
17
- }
18
-
19
- export function encrypt(key: Buffer, nonce: Buffer, aad: Buffer | null, plaintext: Buffer): EncryptedData {
20
- nonce = padNonce(nonce);
21
-
22
- const cipher = createCipher(key, nonce);
23
- aad && cipher.setAAD(aad);
24
-
25
- const ciphertext = cipher._update(plaintext);
26
- cipher._final();
27
-
28
- const authTag = cipher.getAuthTag();
29
-
30
- return {
31
- ciphertext: ciphertext,
32
- authTag: authTag
33
- };
34
- }
35
-
36
- export function padNonce(nonce: Buffer): Buffer {
37
- if (nonce.length >= NONCE_LENGTH) {
38
- return nonce;
39
- }
40
-
41
- return Buffer.concat([
42
- Buffer.alloc(NONCE_LENGTH - nonce.length, 0),
43
- nonce
44
- ]);
45
- }
46
-
47
- // NOTE
48
- // Uncomment when Bun supports chacha20-poly1305 out of box.
49
- //
50
- // import { createCipheriv, createDecipheriv } from 'node:crypto';
51
- //
52
- // export function decrypt(key: Buffer, nonce: Buffer, aad: Buffer | null, ciphertext: Buffer, authTag: Buffer): Buffer {
53
- // if (nonce.length < NONCE_LENGTH) {
54
- // nonce = Buffer.concat([
55
- // Buffer.alloc(NONCE_LENGTH - nonce.length, 0),
56
- // nonce
57
- // ]);
58
- // }
59
- //
60
- // const decipher = createDecipheriv('chacha20-poly1305', key, nonce, {authTagLength: AUTH_TAG_LENGTH});
61
- // aad && decipher.setAAD(aad, {plaintextLength: ciphertext.length});
62
- // decipher.setAuthTag(authTag);
63
- //
64
- // const plaintext = decipher.update(ciphertext);
65
- // decipher.final();
66
- //
67
- // return plaintext;
68
- // }
69
- //
70
- // export function encrypt(key: Buffer, nonce: Buffer, aad: Buffer | null, plaintext: Buffer): EncryptedData {
71
- // if (nonce.length < NONCE_LENGTH) {
72
- // nonce = Buffer.concat([
73
- // Buffer.alloc(NONCE_LENGTH - nonce.length, 0),
74
- // nonce
75
- // ]);
76
- // }
77
- //
78
- // const cipher = createCipheriv('chacha20-poly1305', key, nonce, {authTagLength: AUTH_TAG_LENGTH});
79
- // aad && cipher.setAAD(aad, {plaintextLength: plaintext.length});
80
- //
81
- // const ciphertext = cipher.update(plaintext);
82
- // cipher.final();
83
- //
84
- // const authTag = cipher.getAuthTag();
85
- //
86
- // return {
87
- // ciphertext: ciphertext,
88
- // authTag: authTag
89
- // };
90
- // }
91
-
92
- export type EncryptedData = {
93
- readonly ciphertext: Buffer;
94
- readonly authTag: Buffer;
95
- };
@@ -1,21 +0,0 @@
1
- import { randomBytes } from 'node:crypto';
2
- import { x25519 } from '@noble/curves/ed25519.js';
3
-
4
- export function generateKeyPair(): KeyPair {
5
- const secretKey = randomBytes(32);
6
- const publicKey = x25519.getPublicKey(secretKey);
7
-
8
- return {
9
- publicKey,
10
- secretKey
11
- };
12
- }
13
-
14
- export function generateSharedSecKey(priKey: Uint8Array, pubKey: Uint8Array): Uint8Array {
15
- return x25519.getSharedSecret(priKey, pubKey);
16
- }
17
-
18
- interface KeyPair {
19
- readonly publicKey: Uint8Array;
20
- readonly secretKey: Uint8Array;
21
- }
@@ -1,13 +0,0 @@
1
- import { hkdfSync } from 'node:crypto';
2
-
3
- export default function (options: HKDFOptions): Buffer {
4
- return Buffer.from(hkdfSync(options.hash, options.key, options.salt, options.info, options.length));
5
- }
6
-
7
- export type HKDFOptions = {
8
- readonly hash: string;
9
- readonly key: Buffer;
10
- readonly length: number;
11
- readonly salt: Buffer;
12
- readonly info: Buffer;
13
- };
@@ -1,13 +0,0 @@
1
- export {
2
- decrypt as decryptChacha20,
3
- encrypt as encryptChacha20
4
- } from './chacha20';
5
-
6
- export {
7
- generateKeyPair as generateCurve25519KeyPair,
8
- generateSharedSecKey as generateCurve25519SharedSecKey
9
- } from './curve25519'
10
-
11
- export {
12
- default as hkdf
13
- } from './hkdf';
@@ -1,52 +0,0 @@
1
- import mdns, { Result } from 'node-dns-sd';
2
- import { waitFor } from '@/cli';
3
- import { AIRPLAY_SERVICE, COMPANION_LINK_SERVICE, RAOP_SERVICE } from '@/const';
4
-
5
- export default class Discovery {
6
- readonly #service: string;
7
-
8
- constructor(service: string) {
9
- this.#service = service;
10
- }
11
-
12
- async find(): Promise<Result[]> {
13
- return await mdns.discover({
14
- name: this.#service
15
- });
16
- }
17
-
18
- async findUntil(fqdn: string, tries: number = 10, timeout: number = 1000): Promise<Result> {
19
- while (tries > 0) {
20
- const devices = await this.find();
21
- const device = devices.find(device => device.fqdn === fqdn);
22
-
23
- if (device) {
24
- return device;
25
- }
26
-
27
- console.log();
28
- console.log(`Device not found, retrying in ${timeout}ms...`);
29
- console.log(devices.map(d => ` ● ${d.fqdn}`).join('\n'));
30
-
31
- tries--;
32
-
33
- if (tries === 0) {
34
- throw new Error('Device not found after serveral tries, aborting.');
35
- }
36
-
37
- await waitFor(timeout);
38
- }
39
- }
40
-
41
- static airplay(): Discovery {
42
- return new Discovery(AIRPLAY_SERVICE);
43
- }
44
-
45
- static companionLink(): Discovery {
46
- return new Discovery(COMPANION_LINK_SERVICE);
47
- }
48
-
49
- static raop(): Discovery {
50
- return new Discovery(RAOP_SERVICE);
51
- }
52
- }
@@ -1 +0,0 @@
1
- export { default as Discovery } from './discovery';
@@ -1,22 +0,0 @@
1
- export {
2
- unpack as decodeOPack,
3
- pack as encodeOPack,
4
- float as opackFloat,
5
- int as opackInt,
6
- sizedInt as opackSizedInt
7
- } from './opack';
8
-
9
- export {
10
- parse as parseBinaryPlist,
11
- serialize as serializeBinaryPlist
12
- } from './plist';
13
-
14
- export {
15
- bail as bailTlv,
16
- decode as decodeTlv,
17
- encode as encodeTlv,
18
- Flags as TlvFlags,
19
- Method as TlvMethod,
20
- State as TlvState,
21
- Value as TlvValue
22
- } from './tlv8';