@basmilius/apple-common 0.0.77 → 0.0.78
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/dist/cli.d.ts +2 -1
- package/dist/connection.d.ts +35 -0
- package/dist/const.d.ts +1 -0
- package/dist/crypto/chacha20.d.ts +2 -0
- package/dist/index.d.ts +3 -1
- package/dist/index.js +2 -2
- package/dist/net/index.d.ts +0 -1
- package/dist/symbols.d.ts +1 -0
- package/package.json +2 -2
- package/dist/net/socket.d.ts +0 -19
package/dist/cli.d.ts
CHANGED
|
@@ -8,6 +8,7 @@ declare class Reporter {
|
|
|
8
8
|
disable(group: DebugGroup): void;
|
|
9
9
|
enable(group: DebugGroup): void;
|
|
10
10
|
isEnabled(group: DebugGroup): boolean;
|
|
11
|
+
debug(...data: any[]): void;
|
|
11
12
|
error(...data: any[]): void;
|
|
12
13
|
info(...data: any[]): void;
|
|
13
14
|
net(...data: any[]): void;
|
|
@@ -15,5 +16,5 @@ declare class Reporter {
|
|
|
15
16
|
warn(...data: any[]): void;
|
|
16
17
|
}
|
|
17
18
|
export declare const reporter: Reporter;
|
|
18
|
-
type DebugGroup = "error" | "info" | "net" | "raw" | "warn";
|
|
19
|
+
type DebugGroup = "debug" | "error" | "info" | "net" | "raw" | "warn";
|
|
19
20
|
export {};
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { EventEmitter } from "node:events";
|
|
2
|
+
import type { ConnectionState, EventMap } from "./types";
|
|
3
|
+
type ConnectionEventMap = {
|
|
4
|
+
close: [hadError: boolean];
|
|
5
|
+
connect: [];
|
|
6
|
+
data: [data: Buffer];
|
|
7
|
+
end: [];
|
|
8
|
+
error: [err: Error];
|
|
9
|
+
timeout: [];
|
|
10
|
+
};
|
|
11
|
+
export declare class Connection<TEventMap extends EventMap> extends EventEmitter<ConnectionEventMap | TEventMap> {
|
|
12
|
+
#private;
|
|
13
|
+
get address(): string;
|
|
14
|
+
get port(): number;
|
|
15
|
+
get isConnected(): boolean;
|
|
16
|
+
get state(): ConnectionState;
|
|
17
|
+
constructor(address: string, port: number);
|
|
18
|
+
connect(): Promise<void>;
|
|
19
|
+
disconnect(): Promise<void>;
|
|
20
|
+
debug(enabled: boolean): this;
|
|
21
|
+
retry(attempts: number, interval?: number): this;
|
|
22
|
+
write(data: Buffer | Uint8Array): Promise<void>;
|
|
23
|
+
}
|
|
24
|
+
export declare class EncryptionAwareConnection<TEventMap extends EventMap> extends Connection<TEventMap> {
|
|
25
|
+
get isEncrypted(): boolean;
|
|
26
|
+
enableEncryption(readKey: Buffer, writeKey: Buffer): Promise<void>;
|
|
27
|
+
}
|
|
28
|
+
export declare class EncryptionState {
|
|
29
|
+
readKey: Buffer;
|
|
30
|
+
readCount: number;
|
|
31
|
+
writeKey: Buffer;
|
|
32
|
+
writeCount: number;
|
|
33
|
+
constructor(readKey: Buffer, writeKey: Buffer);
|
|
34
|
+
}
|
|
35
|
+
export {};
|
package/dist/const.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
export declare const AIRPLAY_TRANSIENT_PIN = "3939";
|
|
2
2
|
export declare const HTTP_TIMEOUT = 1750;
|
|
3
|
+
export declare const SOCKET_TIMEOUT = 1e4;
|
|
3
4
|
export declare const AIRPLAY_SERVICE = "_airplay._tcp.local";
|
|
4
5
|
export declare const COMPANION_LINK_SERVICE = "_companion-link._tcp.local";
|
|
5
6
|
export declare const RAOP_SERVICE = "_raop._tcp.local";
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
export declare const CHACHA20_AUTH_TAG_LENGTH = 16;
|
|
2
|
+
export declare const CHACHA20_NONCE_LENGTH = 12;
|
|
1
3
|
export declare function decrypt(key: Buffer, nonce: Buffer, add: Buffer | null, ciphertext: Buffer, authTag: Buffer): Buffer;
|
|
2
4
|
export declare function encrypt(key: Buffer, nonce: Buffer, aad: Buffer | null, plaintext: Buffer): EncryptedData;
|
|
3
5
|
export declare function padNonce(nonce: Buffer): Buffer;
|
package/dist/index.d.ts
CHANGED
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
export { v4 as uuid } from "uuid";
|
|
2
2
|
export { Chacha20, Curve25519, hkdf } from "./crypto";
|
|
3
3
|
export { type DiscoveryResult, Discovery } from "./discovery";
|
|
4
|
-
export { getLocalIP, getMacAddress,
|
|
4
|
+
export { getLocalIP, getMacAddress, TimingServer } from "./net";
|
|
5
5
|
export { prompt, waitFor, cli, reporter } from "./cli";
|
|
6
|
+
export { Connection, EncryptionAwareConnection, EncryptionState } from "./connection";
|
|
6
7
|
export { AIRPLAY_SERVICE, AIRPLAY_TRANSIENT_PIN, COMPANION_LINK_SERVICE, HTTP_TIMEOUT, RAOP_SERVICE } from "./const";
|
|
7
8
|
export { type AccessoryCredentials, type AccessoryKeys, AccessoryPair, AccessoryVerify } from "./pairing";
|
|
9
|
+
export { ENCRYPTION } from "./symbols";
|
|
8
10
|
export { uint16ToBE, uint53ToLE } from "./utils";
|
package/dist/index.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
var
|
|
2
|
-
`)),r--,r===0)throw Error("Device not found after serveral tries, aborting.");await v(t)}}static airplay(){return new l(w)}static companionLink(){return new l(I)}static raop(){return new l(E)}}import{networkInterfaces as Q}from"node:os";function M(){let e=Q();for(let r of Object.keys(e)){let t=e[r];if(!t)continue;for(let o of t){if(o.internal||o.family!=="IPv4")continue;if(o.address&&o.address!=="127.0.0.1")return o.address}}return null}import{networkInterfaces as Z}from"node:os";function V(){let e=Z();for(let r of Object.keys(e)){let t=e[r];if(!t)continue;for(let o of t){if(o.internal||o.family!=="IPv4")continue;if(o.mac&&o.mac!=="00:00:00:00:00:00")return o.mac.toUpperCase()}}return"00:00:00:00:00:00"}import{EventEmitter as ee}from"node:events";class g extends ee{get address(){return this.#e}get port(){return this.#r}#e;#r;constructor(e,r){super();this.#e=e,this.#r=r}async connect(){}async disconnect(){}async onClose(){this.emit("close")}async onConnect(){this.emit("connect")}async onError(e){this.emit("error",e)}async onTimeout(){this.emit("timeout")}}import{createSocket as re}from"node:dgram";import{NTP as b}from"@basmilius/apple-encoding";class _{get port(){return this.#r}#e;#r=0;constructor(){this.#e=re("udp4"),this.#e.on("error",(e)=>this.#t(e)),this.#e.on("message",(e,r)=>this.#n(e,r))}async close(){this.#e.close(),this.#r=0}async listen(){return new Promise((e)=>{this.#e.once("listening",()=>this.#o()),this.#e.bind(0,e)})}async#t(e){m.error("Timing server error",e)}async#o(){let{port:e}=this.#e.address();this.#r=e}async#n(e,r){let t=b.decode(e),o=b.now(),[n,a]=b.parts(o);m.info(`Timing server ntp=${o} receivedSeconds=${n} receivedFraction=${a}`);let i=b.encode({proto:t.proto,type:211,seqno:t.seqno,padding:0,reftime_sec:t.sendtime_sec,reftime_frac:t.sendtime_frac,recvtime_sec:n,recvtime_frac:a,sendtime_sec:n,sendtime_frac:a});this.#e.send(i,r.port,r.address)}}import{OPack as te,TLV8 as s}from"@basmilius/apple-encoding";import{SRP as D,SrpClient as oe}from"fast-srp-hap";import{v4 as se}from"uuid";import h from"tweetnacl";class k{#e;#r;#t;#o;#n;#s;constructor(e){this.#e="basmilius/apple-protocols",this.#r=Buffer.from(se().toUpperCase()),this.#t=e}async start(){let e=h.sign.keyPair();this.#o=Buffer.from(e.publicKey),this.#n=Buffer.from(e.secretKey)}async pin(e){let r=await this.m1(),t=await this.m2(r,await e()),o=await this.m3(t),n=await this.m4(o),a=await this.m5(n),i=await this.m6(n,a);if(!i)throw Error("Pairing failed, could not get accessory keys.");return i}async transient(){let e=await this.m1([[s.Value.Flags,s.Flags.TransientPairing]]),r=await this.m2(e),t=await this.m3(r),o=await this.m4(t),n=c({hash:"sha512",key:o.sharedSecret,length:32,salt:Buffer.from("Control-Salt"),info:Buffer.from("Control-Read-Encryption-Key")}),a=c({hash:"sha512",key:o.sharedSecret,length:32,salt:Buffer.from("Control-Salt"),info:Buffer.from("Control-Write-Encryption-Key")});return{pairingId:this.#r,sharedSecret:o.sharedSecret,accessoryToControllerKey:n,controllerToAccessoryKey:a}}async m1(e=[]){let r=await this.#t("m1",s.encode([[s.Value.Method,s.Method.PairSetup],[s.Value.State,s.State.M1],...e])),t=B(r),o=t.get(s.Value.PublicKey),n=t.get(s.Value.Salt);return{publicKey:o,salt:n}}async m2(e,r=K){let t=await D.genKey(32);this.#s=new oe(D.params.hap,e.salt,Buffer.from("Pair-Setup"),Buffer.from(r),t,!0),this.#s.setB(e.publicKey);let o=this.#s.computeA(),n=this.#s.computeM1();return{publicKey:o,proof:n}}async m3(e){let r=await this.#t("m3",s.encode([[s.Value.State,s.State.M3],[s.Value.PublicKey,e.publicKey],[s.Value.Proof,e.proof]]));return{serverProof:B(r).get(s.Value.Proof)}}async m4(e){return this.#s.checkM2(e.serverProof),{sharedSecret:this.#s.computeK()}}async m5(e){let r=c({hash:"sha512",key:e.sharedSecret,length:32,salt:Buffer.from("Pair-Setup-Controller-Sign-Salt","utf8"),info:Buffer.from("Pair-Setup-Controller-Sign-Info","utf8")}),t=c({hash:"sha512",key:e.sharedSecret,length:32,salt:Buffer.from("Pair-Setup-Encrypt-Salt","utf8"),info:Buffer.from("Pair-Setup-Encrypt-Info","utf8")}),o=Buffer.concat([r,this.#r,this.#o]),n=h.sign.detached(o,this.#n),a=s.encode([[s.Value.Identifier,this.#r],[s.Value.PublicKey,this.#o],[s.Value.Signature,Buffer.from(n)],[s.Value.Name,Buffer.from(te.encode({name:this.#e}))]]),{authTag:i,ciphertext:y}=u.encrypt(t,Buffer.from("PS-Msg05"),null,a),f=Buffer.concat([y,i]),p=await this.#t("m5",s.encode([[s.Value.State,s.State.M5],[s.Value.EncryptedData,f]])),P=B(p).get(s.Value.EncryptedData),N=P.subarray(0,-16);return{authTag:P.subarray(-16),data:N,sessionKey:t}}async m6(e,r){let t=u.decrypt(r.sessionKey,Buffer.from("PS-Msg06"),null,r.data,r.authTag),o=s.decode(t),n=o.get(s.Value.Identifier),a=o.get(s.Value.PublicKey),i=o.get(s.Value.Signature),y=c({hash:"sha512",key:e.sharedSecret,length:32,salt:Buffer.from("Pair-Setup-Accessory-Sign-Salt"),info:Buffer.from("Pair-Setup-Accessory-Sign-Info")}),f=Buffer.concat([y,n,a]);if(!h.sign.detached.verify(f,i,a))throw Error("Invalid accessory signature.");return{accessoryIdentifier:n.toString(),accessoryLongTermPublicKey:a,pairingId:this.#r,publicKey:this.#o,secretKey:this.#n}}}class L{#e;#r;constructor(e){this.#e=d.generateKeyPair(),this.#r=e}async start(e){let r=await this.#t(),t=await this.#o(e.accessoryIdentifier,e.accessoryLongTermPublicKey,r);return await this.#n(e.pairingId,e.secretKey,t),await this.#s(t,e.pairingId)}async#t(){let e=await this.#r("m1",s.encode([[s.Value.State,s.State.M1],[s.Value.PublicKey,Buffer.from(this.#e.publicKey)]])),r=B(e),t=r.get(s.Value.PublicKey);return{encryptedData:r.get(s.Value.EncryptedData),serverPublicKey:t}}async#o(e,r,t){let o=Buffer.from(d.generateSharedSecKey(this.#e.secretKey,t.serverPublicKey)),n=c({hash:"sha512",key:o,length:32,salt:Buffer.from("Pair-Verify-Encrypt-Salt"),info:Buffer.from("Pair-Verify-Encrypt-Info")}),a=t.encryptedData.subarray(0,-16),i=t.encryptedData.subarray(-16),y=u.decrypt(n,Buffer.from("PV-Msg02"),null,a,i),f=s.decode(y),p=f.get(s.Value.Identifier),A=f.get(s.Value.Signature);if(p.toString()!==e)throw Error(`Invalid accessory identifier. Expected ${p.toString()} to be ${e}.`);let P=Buffer.concat([t.serverPublicKey,p,this.#e.publicKey]);if(!h.sign.detached.verify(P,A,r))throw Error("Invalid accessory signature.");return{serverEphemeralPublicKey:t.serverPublicKey,sessionKey:n,sharedSecret:o}}async#n(e,r,t){let o=Buffer.concat([this.#e.publicKey,e,t.serverEphemeralPublicKey]),n=Buffer.from(h.sign.detached(o,r)),a=s.encode([[s.Value.Identifier,e],[s.Value.Signature,n]]),{authTag:i,ciphertext:y}=u.encrypt(t.sessionKey,Buffer.from("PV-Msg03"),null,a),f=Buffer.concat([y,i]);return await this.#r("m3",s.encode([[s.Value.State,s.State.M3],[s.Value.EncryptedData,f]])),{}}async#s(e,r){return{accessoryToControllerKey:Buffer.alloc(0),controllerToAccessoryKey:Buffer.alloc(0),pairingId:r,sharedSecret:e.sharedSecret}}}function B(e){let r=s.decode(e);if(r.has(s.Value.Error))s.bail(r);return m.raw("Decoded TLV",r),r}function ne(e){let r=Buffer.alloc(2);return r.writeUInt16BE(e,0),r}function ae(e){let[r,t]=ie(e),o=Buffer.alloc(8);return o.writeUInt32LE(t,0),o.writeUInt32LE(r,4),o}function ie(e){if(e<=-1||e>9007199254740991)throw Error("Number out of range.");if(Math.floor(e)!==e)throw Error("Number is not an integer.");let o=0,n=e&4294967295,a=n<0?(e&2147483647)+2147483648:n;if(e>4294967295)o=(e-a)/4294967296;return[o,a]}export{v as waitFor,er as uuid,ae as uint53ToLE,ne as uint16ToBE,m as reporter,W as prompt,c as hkdf,V as getMacAddress,M as getLocalIP,R as cli,_ as TimingServer,E as RAOP_SERVICE,z as HTTP_TIMEOUT,l as Discovery,d as Curve25519,u as Chacha20,I as COMPANION_LINK_SERVICE,g as BaseSocket,L as AccessoryVerify,k as AccessoryPair,K as AIRPLAY_TRANSIENT_PIN,w as AIRPLAY_SERVICE};
|
|
1
|
+
var k=Object.defineProperty;var R=(e,r)=>{for(var o in r)k(e,o,{get:r[o],enumerable:!0,configurable:!0,set:(t)=>r[o]=()=>t})};import{v4 as ur}from"uuid";var l={};R(l,{padNonce:()=>w,encrypt:()=>z,decrypt:()=>W,CHACHA20_NONCE_LENGTH:()=>S,CHACHA20_AUTH_TAG_LENGTH:()=>G});import{createCipher as H,createDecipher as Y}from"chacha";var G=16,S=12;function W(e,r,o,t,n){r=w(r);let i=Y(e,r);o&&i.setAAD(o),i.setAuthTag(n);let a=i._update(t);return i._final(),a}function z(e,r,o,t){r=w(r);let n=H(e,r);o&&n.setAAD(o);let i=n._update(t);n._final();let a=n.getAuthTag();return{ciphertext:i,authTag:a}}function w(e){if(e.length>=S)return e;return Buffer.concat([Buffer.alloc(S-e.length,0),e])}var m={};R(m,{generateSharedSecKey:()=>Q,generateKeyPair:()=>J});import D from"tweetnacl";function J(){let e=D.box.keyPair();return{publicKey:e.publicKey,secretKey:e.secretKey}}function Q(e,r){return D.scalarMult(e,r)}import{hkdfSync as Z}from"node:crypto";function c(e){return Buffer.from(Z(e.hash,e.key,e.salt,e.info,e.length))}import oe from"node-dns-sd";import{createInterface as j}from"node:readline";var _=j({input:process.stdin,output:process.stdout});async function ee(e){return await new Promise((r)=>_.question(`${e}: `,r))}async function I(e){return new Promise((r)=>setTimeout(r,e))}class C{#e=[];all(){this.#e=["debug","error","info","net","raw","warn"]}disable(e){if(this.#e.includes(e))this.#e.splice(this.#e.indexOf(e),1)}enable(e){if(!this.#e.includes(e))this.#e.push(e)}isEnabled(e){return this.#e.includes(e)}debug(...e){this.isEnabled("debug")&&console.debug("\x1B[36m[debug]\x1B[39m",...e)}error(...e){this.isEnabled("error")&&console.error("\x1B[31m[error]\x1B[39m",...e)}info(...e){this.isEnabled("info")&&console.info("\x1B[32m[info]\x1B[39m",...e)}net(...e){this.isEnabled("net")&&console.info("\x1B[33m[net]\x1B[39m",...e)}raw(...e){this.isEnabled("raw")&&console.log("\x1B[34m[raw]\x1B[39m",...e)}warn(...e){this.isEnabled("warn")&&console.warn("\x1B[33m[warn]\x1B[39m",...e)}}var f=new C;var K="3939",re=1750,L=1e4,v="_airplay._tcp.local",V="_companion-link._tcp.local",M="_raop._tcp.local";class d{#e;constructor(e){this.#e=e}async find(){return await oe.discover({name:this.#e})}async findUntil(e,r=10,o=1000){while(r>0){let t=await this.find(),n=t.find((i)=>i.fqdn===e);if(n)return n;if(console.log(),console.log(`Device not found, retrying in ${o}ms...`),console.log(t.map((i)=>` ● ${i.fqdn}`).join(`
|
|
2
|
+
`)),r--,r===0)throw Error("Device not found after serveral tries, aborting.");await I(o)}}static airplay(){return new d(v)}static companionLink(){return new d(V)}static raop(){return new d(M)}}import{networkInterfaces as te}from"node:os";function x(){let e=te();for(let r of Object.keys(e)){let o=e[r];if(!o)continue;for(let t of o){if(t.internal||t.family!=="IPv4")continue;if(t.address&&t.address!=="127.0.0.1")return t.address}}return null}import{networkInterfaces as se}from"node:os";function F(){let e=se();for(let r of Object.keys(e)){let o=e[r];if(!o)continue;for(let t of o){if(t.internal||t.family!=="IPv4")continue;if(t.mac&&t.mac!=="00:00:00:00:00:00")return t.mac.toUpperCase()}}return"00:00:00:00:00:00"}import{createSocket as ne}from"node:dgram";import{NTP as B}from"@basmilius/apple-encoding";class N{get port(){return this.#t}#e;#t=0;constructor(){this.#e=ne("udp4"),this.#e.on("error",(e)=>this.#s(e)),this.#e.on("message",(e,r)=>this.#n(e,r))}async close(){this.#e.close(),this.#t=0}async listen(){return new Promise((e)=>{this.#e.once("listening",()=>this.#a()),this.#e.bind(0,e)})}async#s(e){f.error("Timing server error",e)}async#a(){let{port:e}=this.#e.address();this.#t=e}async#n(e,r){let o=B.decode(e),t=B.now(),[n,i]=B.parts(t);f.info(`Timing server ntp=${t} receivedSeconds=${n} receivedFraction=${i}`);let a=B.encode({proto:o.proto,type:211,seqno:o.seqno,padding:0,reftime_sec:o.sendtime_sec,reftime_frac:o.sendtime_frac,recvtime_sec:n,recvtime_frac:i,sendtime_sec:n,sendtime_frac:i});this.#e.send(a,r.port,r.address)}}import{EventEmitter as ie}from"node:events";import{Socket as ae}from"node:net";var h=Symbol();class A extends ie{get address(){return this.#e}get port(){return this.#t}get isConnected(){return this.#o==="connected"}get state(){return this.#o}#e;#t;#s;#a=!1;#n=0;#i=3;#u=!0;#y=3000;#c;#r;#o;#f;constructor(e,r){super();this.#e=e,this.#t=r,this.#s={onClose:this.#p.bind(this),onConnect:this.#h.bind(this),onData:this.#g.bind(this),onEnd:this.#P.bind(this),onError:this.#B.bind(this),onTimeout:this.#b.bind(this)},this.#o="disconnected"}async connect(){if(this.#o==="connected")return;if(this.#o==="connecting")throw Error("A connection is already being established.");return this.#u=!0,this.#n=0,this.#m()}async disconnect(){if(this.#c)clearTimeout(this.#c),this.#c=void 0;if(this.#u=!1,!this.#r||this.#o==="disconnected")return;return new Promise((e)=>{this.#o="closing",this.#r.once("close",()=>{this.#l(),e()}),this.#r.end()})}debug(e){return this.#a=e,this}retry(e,r=3000){return this.#i=e,this.#y=r,this}async write(e){if(!this.#r||this.#o!=="connected")throw Error("Cannot write to a disconnected connection.");return new Promise((r,o)=>{this.#r.write(e,(t)=>t?o(t):r())})}async#m(){return new Promise((e,r)=>{this.#o="connecting",this.#f={resolve:e,reject:r},this.#r=new ae,this.#r.setTimeout(L),this.#r.on("close",this.#s.onClose),this.#r.on("connect",this.#s.onConnect),this.#r.on("data",this.#s.onData),this.#r.on("end",this.#s.onEnd),this.#r.on("error",this.#s.onError),this.#r.on("timeout",this.#s.onTimeout),f.net(`Connecting to ${this.#e}:${this.#t}...`),this.#r.connect({host:this.#e,port:this.#t,keepAlive:!0})})}#l(){if(this.#r)this.#r.removeAllListeners(),this.#r.destroy(),this.#r=void 0;this.#o="disconnected",this.#f=void 0}#d(e){if(!this.#u||this.#n>=this.#i){this.#o="failed",this.#f?.reject(e),this.#f=void 0;return}this.#n++,f.net(`Retry attempt ${this.#n} / ${this.#i} in ${this.#y}ms...`);let{resolve:r,reject:o}=this.#f;this.#l(),this.#c=setTimeout(async()=>{this.#c=void 0;try{this.#f={resolve:r,reject:o},await this.#m(),r()}catch(t){}},this.#y)}#p(e){let r=this.#o==="connected";if(this.#o!=="closing")this.#o="disconnected";if(this.emit("close",e),r&&this.#u&&e)this.#d(Error("Connection closed unexpectedly."))}#h(){this.#o="connected",this.#n=0,this.#r.setKeepAlive(!0,1e4),this.#r.setTimeout(0),this.emit("connect"),this.#f?.resolve(),this.#f=void 0}#g(e){if(this.#a){let r=Math.min(e.byteLength,64);f.debug(`Received ${e.byteLength} bytes of data.`),f.debug(`hex=${e.subarray(0,r).toString("hex")}`),f.debug(`ascii=${e.toString("ascii").replace(/[^\x20-\x7E]/g,".").substring(0,r)}`)}this.emit("data",e)}#P(){this.emit("end")}#B(e){if(f.error(`Connection error: ${e.message}`),this.emit("error",e),this.#o==="connecting")this.#l(),this.#d(e);else this.#o="failed"}#b(){f.error("Connection timed out.");let e=Error("Connection timed out.");if(this.emit("timeout"),this.emit("error",e),this.#o==="connecting")this.#l(),this.#d(e);else this.#o="failed",this.#r?.destroy()}}class U extends A{get isEncrypted(){return!!this[h]}[h];async enableEncryption(e,r){this[h]=new E(e,r)}}class E{readKey;readCount;writeKey;writeCount;constructor(e,r){this.readCount=0,this.readKey=e,this.writeCount=0,this.writeKey=r}}import{OPack as fe,TLV8 as s}from"@basmilius/apple-encoding";import{SRP as q,SrpClient as ce}from"fast-srp-hap";import{v4 as ue}from"uuid";import g from"tweetnacl";class O{#e;#t;#s;#a;#n;#i;constructor(e){this.#e="basmilius/apple-protocols",this.#t=Buffer.from(ue().toUpperCase()),this.#s=e}async start(){let e=g.sign.keyPair();this.#a=Buffer.from(e.publicKey),this.#n=Buffer.from(e.secretKey)}async pin(e){let r=await this.m1(),o=await this.m2(r,await e()),t=await this.m3(o),n=await this.m4(t),i=await this.m5(n),a=await this.m6(n,i);if(!a)throw Error("Pairing failed, could not get accessory keys.");return a}async transient(){let e=await this.m1([[s.Value.Flags,s.Flags.TransientPairing]]),r=await this.m2(e),o=await this.m3(r),t=await this.m4(o),n=c({hash:"sha512",key:t.sharedSecret,length:32,salt:Buffer.from("Control-Salt"),info:Buffer.from("Control-Read-Encryption-Key")}),i=c({hash:"sha512",key:t.sharedSecret,length:32,salt:Buffer.from("Control-Salt"),info:Buffer.from("Control-Write-Encryption-Key")});return{pairingId:this.#t,sharedSecret:t.sharedSecret,accessoryToControllerKey:n,controllerToAccessoryKey:i}}async m1(e=[]){let r=await this.#s("m1",s.encode([[s.Value.Method,s.Method.PairSetup],[s.Value.State,s.State.M1],...e])),o=b(r),t=o.get(s.Value.PublicKey),n=o.get(s.Value.Salt);return{publicKey:t,salt:n}}async m2(e,r=K){let o=await q.genKey(32);this.#i=new ce(q.params.hap,e.salt,Buffer.from("Pair-Setup"),Buffer.from(r),o,!0),this.#i.setB(e.publicKey);let t=this.#i.computeA(),n=this.#i.computeM1();return{publicKey:t,proof:n}}async m3(e){let r=await this.#s("m3",s.encode([[s.Value.State,s.State.M3],[s.Value.PublicKey,e.publicKey],[s.Value.Proof,e.proof]]));return{serverProof:b(r).get(s.Value.Proof)}}async m4(e){return this.#i.checkM2(e.serverProof),{sharedSecret:this.#i.computeK()}}async m5(e){let r=c({hash:"sha512",key:e.sharedSecret,length:32,salt:Buffer.from("Pair-Setup-Controller-Sign-Salt","utf8"),info:Buffer.from("Pair-Setup-Controller-Sign-Info","utf8")}),o=c({hash:"sha512",key:e.sharedSecret,length:32,salt:Buffer.from("Pair-Setup-Encrypt-Salt","utf8"),info:Buffer.from("Pair-Setup-Encrypt-Info","utf8")}),t=Buffer.concat([r,this.#t,this.#a]),n=g.sign.detached(t,this.#n),i=s.encode([[s.Value.Identifier,this.#t],[s.Value.PublicKey,this.#a],[s.Value.Signature,Buffer.from(n)],[s.Value.Name,Buffer.from(fe.encode({name:this.#e}))]]),{authTag:a,ciphertext:y}=l.encrypt(o,Buffer.from("PS-Msg05"),null,i),u=Buffer.concat([y,a]),p=await this.#s("m5",s.encode([[s.Value.State,s.State.M5],[s.Value.EncryptedData,u]])),P=b(p).get(s.Value.EncryptedData),X=P.subarray(0,-16);return{authTag:P.subarray(-16),data:X,sessionKey:o}}async m6(e,r){let o=l.decrypt(r.sessionKey,Buffer.from("PS-Msg06"),null,r.data,r.authTag),t=s.decode(o),n=t.get(s.Value.Identifier),i=t.get(s.Value.PublicKey),a=t.get(s.Value.Signature),y=c({hash:"sha512",key:e.sharedSecret,length:32,salt:Buffer.from("Pair-Setup-Accessory-Sign-Salt"),info:Buffer.from("Pair-Setup-Accessory-Sign-Info")}),u=Buffer.concat([y,n,i]);if(!g.sign.detached.verify(u,a,i))throw Error("Invalid accessory signature.");return{accessoryIdentifier:n.toString(),accessoryLongTermPublicKey:i,pairingId:this.#t,publicKey:this.#a,secretKey:this.#n}}}class ${#e;#t;constructor(e){this.#e=m.generateKeyPair(),this.#t=e}async start(e){let r=await this.#s(),o=await this.#a(e.accessoryIdentifier,e.accessoryLongTermPublicKey,r);return await this.#n(e.pairingId,e.secretKey,o),await this.#i(o,e.pairingId)}async#s(){let e=await this.#t("m1",s.encode([[s.Value.State,s.State.M1],[s.Value.PublicKey,Buffer.from(this.#e.publicKey)]])),r=b(e),o=r.get(s.Value.PublicKey);return{encryptedData:r.get(s.Value.EncryptedData),serverPublicKey:o}}async#a(e,r,o){let t=Buffer.from(m.generateSharedSecKey(this.#e.secretKey,o.serverPublicKey)),n=c({hash:"sha512",key:t,length:32,salt:Buffer.from("Pair-Verify-Encrypt-Salt"),info:Buffer.from("Pair-Verify-Encrypt-Info")}),i=o.encryptedData.subarray(0,-16),a=o.encryptedData.subarray(-16),y=l.decrypt(n,Buffer.from("PV-Msg02"),null,i,a),u=s.decode(y),p=u.get(s.Value.Identifier),T=u.get(s.Value.Signature);if(p.toString()!==e)throw Error(`Invalid accessory identifier. Expected ${p.toString()} to be ${e}.`);let P=Buffer.concat([o.serverPublicKey,p,this.#e.publicKey]);if(!g.sign.detached.verify(P,T,r))throw Error("Invalid accessory signature.");return{serverEphemeralPublicKey:o.serverPublicKey,sessionKey:n,sharedSecret:t}}async#n(e,r,o){let t=Buffer.concat([this.#e.publicKey,e,o.serverEphemeralPublicKey]),n=Buffer.from(g.sign.detached(t,r)),i=s.encode([[s.Value.Identifier,e],[s.Value.Signature,n]]),{authTag:a,ciphertext:y}=l.encrypt(o.sessionKey,Buffer.from("PV-Msg03"),null,i),u=Buffer.concat([y,a]);return await this.#t("m3",s.encode([[s.Value.State,s.State.M3],[s.Value.EncryptedData,u]])),{}}async#i(e,r){return{accessoryToControllerKey:Buffer.alloc(0),controllerToAccessoryKey:Buffer.alloc(0),pairingId:r,sharedSecret:e.sharedSecret}}}function b(e){let r=s.decode(e);if(r.has(s.Value.Error))s.bail(r);return f.raw("Decoded TLV",r),r}function le(e){let r=Buffer.alloc(2);return r.writeUInt16BE(e,0),r}function ye(e){let[r,o]=de(e),t=Buffer.alloc(8);return t.writeUInt32LE(o,0),t.writeUInt32LE(r,4),t}function de(e){if(e<=-1||e>9007199254740991)throw Error("Number out of range.");if(Math.floor(e)!==e)throw Error("Number is not an integer.");let t=0,n=e&4294967295,i=n<0?(e&2147483647)+2147483648:n;if(e>4294967295)t=(e-i)/4294967296;return[t,i]}export{I as waitFor,ur as uuid,ye as uint53ToLE,le as uint16ToBE,f as reporter,ee as prompt,c as hkdf,F as getMacAddress,x as getLocalIP,_ as cli,N as TimingServer,M as RAOP_SERVICE,re as HTTP_TIMEOUT,E as EncryptionState,U as EncryptionAwareConnection,h as ENCRYPTION,d as Discovery,m as Curve25519,A as Connection,l as Chacha20,V as COMPANION_LINK_SERVICE,$ as AccessoryVerify,O as AccessoryPair,K as AIRPLAY_TRANSIENT_PIN,v as AIRPLAY_SERVICE};
|
package/dist/net/index.d.ts
CHANGED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const ENCRYPTION: unique symbol;
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@basmilius/apple-common",
|
|
3
3
|
"description": "Common features shared across various apple protocol packages.",
|
|
4
|
-
"version": "0.0.
|
|
4
|
+
"version": "0.0.78",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
7
7
|
"author": {
|
|
@@ -38,7 +38,7 @@
|
|
|
38
38
|
}
|
|
39
39
|
},
|
|
40
40
|
"dependencies": {
|
|
41
|
-
"@basmilius/apple-encoding": "0.0.
|
|
41
|
+
"@basmilius/apple-encoding": "0.0.78",
|
|
42
42
|
"chacha": "^2.1.0",
|
|
43
43
|
"fast-srp-hap": "^2.0.4",
|
|
44
44
|
"node-dns-sd": "^1.0.1",
|
package/dist/net/socket.d.ts
DELETED
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
import { EventEmitter } from "node:events";
|
|
2
|
-
type EventMap = {
|
|
3
|
-
close: [];
|
|
4
|
-
connect: [];
|
|
5
|
-
error: [Error];
|
|
6
|
-
timeout: [];
|
|
7
|
-
};
|
|
8
|
-
export default class BaseSocket<T extends Record<string, any>> extends EventEmitter<T | EventMap> {
|
|
9
|
-
#private;
|
|
10
|
-
get address(): string;
|
|
11
|
-
get port(): number;
|
|
12
|
-
constructor(address: string, port: number);
|
|
13
|
-
connect(): Promise<void>;
|
|
14
|
-
disconnect(): Promise<void>;
|
|
15
|
-
onClose(): Promise<void>;
|
|
16
|
-
onConnect(): Promise<void>;
|
|
17
|
-
onError(err: Error): Promise<void>;
|
|
18
|
-
onTimeout(): Promise<void>;
|
|
19
|
-
}
|