appium-ios-remotexpc 0.0.4 → 0.0.5

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.
Files changed (26) hide show
  1. package/CHANGELOG.md +6 -0
  2. package/build/src/lib/apple-tv/constants.d.ts +28 -0
  3. package/build/src/lib/apple-tv/constants.d.ts.map +1 -1
  4. package/build/src/lib/apple-tv/constants.js +35 -0
  5. package/build/src/lib/apple-tv/encryption/chacha20-poly1305.d.ts +22 -0
  6. package/build/src/lib/apple-tv/encryption/chacha20-poly1305.d.ts.map +1 -0
  7. package/build/src/lib/apple-tv/encryption/chacha20-poly1305.js +97 -0
  8. package/build/src/lib/apple-tv/encryption/ed25519.d.ts +16 -0
  9. package/build/src/lib/apple-tv/encryption/ed25519.d.ts.map +1 -0
  10. package/build/src/lib/apple-tv/encryption/ed25519.js +93 -0
  11. package/build/src/lib/apple-tv/encryption/hkdf.d.ts +18 -0
  12. package/build/src/lib/apple-tv/encryption/hkdf.d.ts.map +1 -0
  13. package/build/src/lib/apple-tv/encryption/hkdf.js +73 -0
  14. package/build/src/lib/apple-tv/encryption/index.d.ts +5 -0
  15. package/build/src/lib/apple-tv/encryption/index.d.ts.map +1 -0
  16. package/build/src/lib/apple-tv/encryption/index.js +4 -0
  17. package/build/src/lib/apple-tv/encryption/opack2.d.ts +57 -0
  18. package/build/src/lib/apple-tv/encryption/opack2.d.ts.map +1 -0
  19. package/build/src/lib/apple-tv/encryption/opack2.js +203 -0
  20. package/package.json +1 -1
  21. package/src/lib/apple-tv/constants.ts +42 -0
  22. package/src/lib/apple-tv/encryption/chacha20-poly1305.ts +147 -0
  23. package/src/lib/apple-tv/encryption/ed25519.ts +126 -0
  24. package/src/lib/apple-tv/encryption/hkdf.ts +95 -0
  25. package/src/lib/apple-tv/encryption/index.ts +11 -0
  26. package/src/lib/apple-tv/encryption/opack2.ts +257 -0
package/CHANGELOG.md CHANGED
@@ -1,3 +1,9 @@
1
+ ## [0.0.5](https://github.com/appium/appium-ios-remotexpc/compare/v0.0.4...v0.0.5) (2025-07-04)
2
+
3
+ ### Miscellaneous Chores
4
+
5
+ * add appletv encryption module for RemoteXPC support ([#50](https://github.com/appium/appium-ios-remotexpc/issues/50)) ([0e5f3c6](https://github.com/appium/appium-ios-remotexpc/commit/0e5f3c6064bb0dc02f05f9349bac942c4c3bc951))
6
+
1
7
  ## [0.0.4](https://github.com/appium/appium-ios-remotexpc/compare/v0.0.3...v0.0.4) (2025-07-03)
2
8
 
3
9
  ### Miscellaneous Chores
@@ -46,4 +46,32 @@ export declare const SRP_KEY_LENGTH_BYTES = 384;
46
46
  export declare const SRP_PRIVATE_KEY_BITS = 256;
47
47
  export declare const HKDF_HASH_ALGORITHM = "sha512";
48
48
  export declare const HKDF_HASH_LENGTH = 64;
49
+ export declare const OPACK2_NULL = 3;
50
+ export declare const OPACK2_TRUE = 1;
51
+ export declare const OPACK2_FALSE = 2;
52
+ export declare const OPACK2_SMALL_INT_OFFSET = 8;
53
+ export declare const OPACK2_SMALL_INT_MAX = 39;
54
+ export declare const OPACK2_SMALL_STRING_MAX = 32;
55
+ export declare const OPACK2_SMALL_BYTES_MAX = 32;
56
+ export declare const OPACK2_SMALL_ARRAY_MAX = 15;
57
+ export declare const OPACK2_SMALL_DICT_MAX = 15;
58
+ export declare const OPACK2_INT8_MARKER = 48;
59
+ export declare const OPACK2_INT32_MARKER = 50;
60
+ export declare const OPACK2_INT64_MARKER = 51;
61
+ export declare const OPACK2_FLOAT_MARKER = 53;
62
+ export declare const OPACK2_SMALL_STRING_BASE = 64;
63
+ export declare const OPACK2_STRING_8BIT_LEN_MARKER = 97;
64
+ export declare const OPACK2_STRING_16BIT_LEN_MARKER = 98;
65
+ export declare const OPACK2_STRING_32BIT_LEN_MARKER = 99;
66
+ export declare const OPACK2_SMALL_BYTES_BASE = 112;
67
+ export declare const OPACK2_BYTES_8BIT_LEN_MARKER = 145;
68
+ export declare const OPACK2_BYTES_16BIT_LEN_MARKER = 146;
69
+ export declare const OPACK2_BYTES_32BIT_LEN_MARKER = 147;
70
+ export declare const OPACK2_SMALL_ARRAY_BASE = 208;
71
+ export declare const OPACK2_VARIABLE_ARRAY_MARKER = 223;
72
+ export declare const OPACK2_SMALL_DICT_BASE = 224;
73
+ export declare const OPACK2_VARIABLE_DICT_MARKER = 239;
74
+ export declare const OPACK2_UINT8_MAX = 255;
75
+ export declare const OPACK2_UINT16_MAX = 65535;
76
+ export declare const OPACK2_UINT32_MAX = 4294967295;
49
77
  //# sourceMappingURL=constants.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"constants.d.ts","sourceRoot":"","sources":["../../../../src/lib/apple-tv/constants.ts"],"names":[],"mappings":"AACA,eAAO,MAAM,sBAAsB;;;;;CAKzB,CAAC;AAGX,eAAO,MAAM,wBAAwB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAgC3B,CAAC;AAGX,eAAO,MAAM,sBAAsB,MAAM,CAAC;AAG1C,eAAO,MAAM,cAAc,QAc1B,CAAC;AAGF,eAAO,MAAM,aAAa,QAAY,CAAC;AAGvC,eAAO,MAAM,kBAAkB,WAAW,CAAC;AAG3C,eAAO,MAAM,YAAY,eAAe,CAAC;AAGzC,eAAO,MAAM,oBAAoB,MAAM,CAAC;AAGxC,eAAO,MAAM,oBAAoB,MAAM,CAAC;AAGxC,eAAO,MAAM,mBAAmB,WAAW,CAAC;AAG5C,eAAO,MAAM,gBAAgB,KAAK,CAAC"}
1
+ {"version":3,"file":"constants.d.ts","sourceRoot":"","sources":["../../../../src/lib/apple-tv/constants.ts"],"names":[],"mappings":"AACA,eAAO,MAAM,sBAAsB;;;;;CAKzB,CAAC;AAGX,eAAO,MAAM,wBAAwB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAgC3B,CAAC;AAGX,eAAO,MAAM,sBAAsB,MAAM,CAAC;AAG1C,eAAO,MAAM,cAAc,QAc1B,CAAC;AAGF,eAAO,MAAM,aAAa,QAAY,CAAC;AAGvC,eAAO,MAAM,kBAAkB,WAAW,CAAC;AAG3C,eAAO,MAAM,YAAY,eAAe,CAAC;AAGzC,eAAO,MAAM,oBAAoB,MAAM,CAAC;AAGxC,eAAO,MAAM,oBAAoB,MAAM,CAAC;AAGxC,eAAO,MAAM,mBAAmB,WAAW,CAAC;AAG5C,eAAO,MAAM,gBAAgB,KAAK,CAAC;AAGnC,eAAO,MAAM,WAAW,IAAO,CAAC;AAChC,eAAO,MAAM,WAAW,IAAO,CAAC;AAChC,eAAO,MAAM,YAAY,IAAO,CAAC;AACjC,eAAO,MAAM,uBAAuB,IAAI,CAAC;AACzC,eAAO,MAAM,oBAAoB,KAAO,CAAC;AACzC,eAAO,MAAM,uBAAuB,KAAO,CAAC;AAC5C,eAAO,MAAM,sBAAsB,KAAO,CAAC;AAC3C,eAAO,MAAM,sBAAsB,KAAK,CAAC;AACzC,eAAO,MAAM,qBAAqB,KAAK,CAAC;AAGxC,eAAO,MAAM,kBAAkB,KAAO,CAAC;AACvC,eAAO,MAAM,mBAAmB,KAAO,CAAC;AACxC,eAAO,MAAM,mBAAmB,KAAO,CAAC;AACxC,eAAO,MAAM,mBAAmB,KAAO,CAAC;AAGxC,eAAO,MAAM,wBAAwB,KAAO,CAAC;AAC7C,eAAO,MAAM,6BAA6B,KAAO,CAAC;AAClD,eAAO,MAAM,8BAA8B,KAAO,CAAC;AACnD,eAAO,MAAM,8BAA8B,KAAO,CAAC;AAGnD,eAAO,MAAM,uBAAuB,MAAO,CAAC;AAC5C,eAAO,MAAM,4BAA4B,MAAO,CAAC;AACjD,eAAO,MAAM,6BAA6B,MAAO,CAAC;AAClD,eAAO,MAAM,6BAA6B,MAAO,CAAC;AAGlD,eAAO,MAAM,uBAAuB,MAAO,CAAC;AAC5C,eAAO,MAAM,4BAA4B,MAAO,CAAC;AAGjD,eAAO,MAAM,sBAAsB,MAAO,CAAC;AAC3C,eAAO,MAAM,2BAA2B,MAAO,CAAC;AAGhD,eAAO,MAAM,gBAAgB,MAAO,CAAC;AACrC,eAAO,MAAM,iBAAiB,QAAS,CAAC;AACxC,eAAO,MAAM,iBAAiB,aAAa,CAAC"}
@@ -69,3 +69,38 @@ export const SRP_PRIVATE_KEY_BITS = 256;
69
69
  export const HKDF_HASH_ALGORITHM = 'sha512';
70
70
  // Output length (in bytes) for HKDF key derivation
71
71
  export const HKDF_HASH_LENGTH = 64;
72
+ // OPACK2 encoding constants
73
+ export const OPACK2_NULL = 0x03;
74
+ export const OPACK2_TRUE = 0x01;
75
+ export const OPACK2_FALSE = 0x02;
76
+ export const OPACK2_SMALL_INT_OFFSET = 8;
77
+ export const OPACK2_SMALL_INT_MAX = 0x27;
78
+ export const OPACK2_SMALL_STRING_MAX = 0x20;
79
+ export const OPACK2_SMALL_BYTES_MAX = 0x20;
80
+ export const OPACK2_SMALL_ARRAY_MAX = 15;
81
+ export const OPACK2_SMALL_DICT_MAX = 15;
82
+ // OPACK2 number type markers
83
+ export const OPACK2_INT8_MARKER = 0x30;
84
+ export const OPACK2_INT32_MARKER = 0x32;
85
+ export const OPACK2_INT64_MARKER = 0x33;
86
+ export const OPACK2_FLOAT_MARKER = 0x35;
87
+ // OPACK2 string type markers
88
+ export const OPACK2_SMALL_STRING_BASE = 0x40;
89
+ export const OPACK2_STRING_8BIT_LEN_MARKER = 0x61;
90
+ export const OPACK2_STRING_16BIT_LEN_MARKER = 0x62;
91
+ export const OPACK2_STRING_32BIT_LEN_MARKER = 0x63;
92
+ // OPACK2 bytes type markers
93
+ export const OPACK2_SMALL_BYTES_BASE = 0x70;
94
+ export const OPACK2_BYTES_8BIT_LEN_MARKER = 0x91;
95
+ export const OPACK2_BYTES_16BIT_LEN_MARKER = 0x92;
96
+ export const OPACK2_BYTES_32BIT_LEN_MARKER = 0x93;
97
+ // OPACK2 array type markers
98
+ export const OPACK2_SMALL_ARRAY_BASE = 0xd0;
99
+ export const OPACK2_VARIABLE_ARRAY_MARKER = 0xdf;
100
+ // OPACK2 dictionary type markers
101
+ export const OPACK2_SMALL_DICT_BASE = 0xe0;
102
+ export const OPACK2_VARIABLE_DICT_MARKER = 0xef;
103
+ // OPACK2 size limits
104
+ export const OPACK2_UINT8_MAX = 0xff;
105
+ export const OPACK2_UINT16_MAX = 0xffff;
106
+ export const OPACK2_UINT32_MAX = 0xffffffff;
@@ -0,0 +1,22 @@
1
+ export interface ChaCha20Poly1305Params {
2
+ plaintext?: Buffer;
3
+ ciphertext?: Buffer;
4
+ key: Buffer;
5
+ nonce: Buffer;
6
+ aad?: Buffer;
7
+ }
8
+ /**
9
+ * Encrypts data using ChaCha20-Poly1305 AEAD cipher
10
+ * @param params - Encryption parameters including plaintext, key, nonce, and optional AAD
11
+ * @returns Buffer containing encrypted data concatenated with authentication tag
12
+ * @throws CryptographyError if encryption fails or required parameters are missing
13
+ */
14
+ export declare function encryptChaCha20Poly1305(params: ChaCha20Poly1305Params): Buffer;
15
+ /**
16
+ * Decrypts data using ChaCha20-Poly1305 AEAD cipher with multiple fallback strategies
17
+ * @param params - Decryption parameters including ciphertext, key, nonce, and optional AAD
18
+ * @returns Buffer containing decrypted plaintext
19
+ * @throws CryptographyError if all decryption attempts fail or required parameters are missing
20
+ */
21
+ export declare function decryptChaCha20Poly1305(params: ChaCha20Poly1305Params): Buffer;
22
+ //# sourceMappingURL=chacha20-poly1305.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"chacha20-poly1305.d.ts","sourceRoot":"","sources":["../../../../../src/lib/apple-tv/encryption/chacha20-poly1305.ts"],"names":[],"mappings":"AAOA,MAAM,WAAW,sBAAsB;IACrC,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,EAAE,MAAM,CAAC;IACd,GAAG,CAAC,EAAE,MAAM,CAAC;CACd;AAOD;;;;;GAKG;AACH,wBAAgB,uBAAuB,CACrC,MAAM,EAAE,sBAAsB,GAC7B,MAAM,CAiCR;AAED;;;;;GAKG;AACH,wBAAgB,uBAAuB,CACrC,MAAM,EAAE,sBAAsB,GAC7B,MAAM,CA2ER"}
@@ -0,0 +1,97 @@
1
+ import { logger } from '@appium/support';
2
+ import { createCipheriv, createDecipheriv } from 'node:crypto';
3
+ import { CryptographyError } from '../errors.js';
4
+ const log = logger.getLogger('ChaCha20Poly1305');
5
+ /**
6
+ * Encrypts data using ChaCha20-Poly1305 AEAD cipher
7
+ * @param params - Encryption parameters including plaintext, key, nonce, and optional AAD
8
+ * @returns Buffer containing encrypted data concatenated with authentication tag
9
+ * @throws CryptographyError if encryption fails or required parameters are missing
10
+ */
11
+ export function encryptChaCha20Poly1305(params) {
12
+ const { plaintext, key, nonce, aad } = params;
13
+ if (!plaintext) {
14
+ throw new CryptographyError('Plaintext is required for encryption');
15
+ }
16
+ if (!key || key.length !== 32) {
17
+ throw new CryptographyError('Key must be 32 bytes');
18
+ }
19
+ if (!nonce || nonce.length !== 12) {
20
+ throw new CryptographyError('Nonce must be 12 bytes');
21
+ }
22
+ try {
23
+ const cipher = createCipheriv('chacha20-poly1305', key, nonce);
24
+ if (aad) {
25
+ cipher.setAAD(aad, { plaintextLength: plaintext.length });
26
+ }
27
+ const encrypted = Buffer.concat([cipher.update(plaintext), cipher.final()]);
28
+ const authTag = cipher.getAuthTag();
29
+ return Buffer.concat([encrypted, authTag]);
30
+ }
31
+ catch (error) {
32
+ log.error('ChaCha20-Poly1305 encryption failed:', error);
33
+ const message = error instanceof Error ? error.message : String(error);
34
+ throw new CryptographyError(`ChaCha20-Poly1305 encryption failed: ${message}`);
35
+ }
36
+ }
37
+ /**
38
+ * Decrypts data using ChaCha20-Poly1305 AEAD cipher with multiple fallback strategies
39
+ * @param params - Decryption parameters including ciphertext, key, nonce, and optional AAD
40
+ * @returns Buffer containing decrypted plaintext
41
+ * @throws CryptographyError if all decryption attempts fail or required parameters are missing
42
+ */
43
+ export function decryptChaCha20Poly1305(params) {
44
+ const { ciphertext, key, nonce, aad } = params;
45
+ if (!ciphertext) {
46
+ throw new CryptographyError('Ciphertext is required for decryption');
47
+ }
48
+ if (!key || key.length !== 32) {
49
+ throw new CryptographyError('Key must be 32 bytes');
50
+ }
51
+ if (!nonce || nonce.length !== 12) {
52
+ throw new CryptographyError('Nonce must be 12 bytes');
53
+ }
54
+ if (ciphertext.length < 16) {
55
+ throw new CryptographyError('Ciphertext too short to contain authentication tag');
56
+ }
57
+ // ChaCha20-Poly1305 in Node.js only supports 16-byte authentication tags
58
+ const tagLength = 16;
59
+ const decryptionAttempts = [
60
+ { tagLen: tagLength, aad },
61
+ { tagLen: tagLength, aad: Buffer.alloc(0) },
62
+ { tagLen: tagLength, aad: undefined },
63
+ ];
64
+ let lastError;
65
+ for (const attempt of decryptionAttempts) {
66
+ try {
67
+ const encrypted = ciphertext.subarray(0, ciphertext.length - attempt.tagLen);
68
+ const authTag = ciphertext.subarray(ciphertext.length - attempt.tagLen);
69
+ const decipher = createDecipheriv('chacha20-poly1305', key, nonce);
70
+ decipher.setAuthTag(authTag);
71
+ if (attempt.aad !== undefined) {
72
+ decipher.setAAD(attempt.aad, { plaintextLength: encrypted.length });
73
+ }
74
+ const decrypted = Buffer.concat([
75
+ decipher.update(encrypted),
76
+ decipher.final(),
77
+ ]);
78
+ log.debug('Decryption successful with AAD:', attempt.aad ? 'provided' : 'none');
79
+ return decrypted;
80
+ }
81
+ catch (error) {
82
+ lastError = error instanceof Error ? error : new Error(String(error));
83
+ }
84
+ }
85
+ const errorMessage = lastError
86
+ ? `ChaCha20-Poly1305 decryption failed: ${lastError.message}`
87
+ : 'ChaCha20-Poly1305 decryption failed: invalid ciphertext or authentication tag';
88
+ // Log the error with stack trace for debugging real failures
89
+ // Skip logging in test environment to avoid cluttering test output with expected failures
90
+ if (lastError && process.env.NODE_ENV !== 'test') {
91
+ log.error('All ChaCha20-Poly1305 decryption attempts failed:', {
92
+ message: lastError.message,
93
+ stack: lastError.stack,
94
+ });
95
+ }
96
+ throw new CryptographyError(errorMessage);
97
+ }
@@ -0,0 +1,16 @@
1
+ import type { PairingKeys } from '../types.js';
2
+ /**
3
+ * Generates a new Ed25519 key pair for cryptographic operations
4
+ * @returns PairingKeys object containing 32-byte public and private key buffers
5
+ * @throws CryptographyError if key generation fails
6
+ */
7
+ export declare function generateEd25519KeyPair(): PairingKeys;
8
+ /**
9
+ * Creates an Ed25519 digital signature for the provided data
10
+ * @param data - The data to sign
11
+ * @param privateKey - 32-byte Ed25519 private key
12
+ * @returns Buffer containing the 64-byte signature
13
+ * @throws CryptographyError if signing fails or private key is invalid
14
+ */
15
+ export declare function createEd25519Signature(data: Buffer, privateKey: Buffer): Buffer;
16
+ //# sourceMappingURL=ed25519.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ed25519.d.ts","sourceRoot":"","sources":["../../../../../src/lib/apple-tv/encryption/ed25519.ts"],"names":[],"mappings":"AAQA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAW/C;;;;GAIG;AACH,wBAAgB,sBAAsB,IAAI,WAAW,CA4BpD;AAED;;;;;;GAMG;AACH,wBAAgB,sBAAsB,CACpC,IAAI,EAAE,MAAM,EACZ,UAAU,EAAE,MAAM,GACjB,MAAM,CA0BR"}
@@ -0,0 +1,93 @@
1
+ import { logger } from '@appium/support';
2
+ import { generateKeyPairSync, sign, } from 'node:crypto';
3
+ import { CryptographyError } from '../errors.js';
4
+ const log = logger.getLogger('Ed25519');
5
+ const ED25519_PUBLIC_KEY_LENGTH = 32;
6
+ const ED25519_PRIVATE_KEY_LENGTH = 32;
7
+ const ED25519_PKCS8_PREFIX = Buffer.from('302e020100300506032b657004220420', 'hex');
8
+ /**
9
+ * Generates a new Ed25519 key pair for cryptographic operations
10
+ * @returns PairingKeys object containing 32-byte public and private key buffers
11
+ * @throws CryptographyError if key generation fails
12
+ */
13
+ export function generateEd25519KeyPair() {
14
+ try {
15
+ const keyPair = generateKeyPairSync('ed25519');
16
+ const publicKeyDer = keyPair.publicKey.export({
17
+ type: 'spki',
18
+ format: 'der',
19
+ });
20
+ const privateKeyDer = keyPair.privateKey.export({
21
+ type: 'pkcs8',
22
+ format: 'der',
23
+ });
24
+ const publicKeyBuffer = extractEd25519PublicKey(publicKeyDer);
25
+ const privateKeyBuffer = extractEd25519PrivateKey(privateKeyDer);
26
+ return {
27
+ publicKey: publicKeyBuffer,
28
+ privateKey: privateKeyBuffer,
29
+ };
30
+ }
31
+ catch (error) {
32
+ log.error('Failed to generate Ed25519 key pair:', error);
33
+ const message = error instanceof Error ? error.message : String(error);
34
+ throw new CryptographyError(`Failed to generate Ed25519 key pair: ${message}`);
35
+ }
36
+ }
37
+ /**
38
+ * Creates an Ed25519 digital signature for the provided data
39
+ * @param data - The data to sign
40
+ * @param privateKey - 32-byte Ed25519 private key
41
+ * @returns Buffer containing the 64-byte signature
42
+ * @throws CryptographyError if signing fails or private key is invalid
43
+ */
44
+ export function createEd25519Signature(data, privateKey) {
45
+ if (!data || data.length === 0) {
46
+ throw new CryptographyError('Data to sign cannot be empty');
47
+ }
48
+ if (!privateKey || privateKey.length !== ED25519_PRIVATE_KEY_LENGTH) {
49
+ throw new CryptographyError(`Private key must be ${ED25519_PRIVATE_KEY_LENGTH} bytes`);
50
+ }
51
+ try {
52
+ const privateKeyDer = Buffer.concat([ED25519_PKCS8_PREFIX, privateKey]);
53
+ return sign(null, data, {
54
+ key: privateKeyDer,
55
+ format: 'der',
56
+ type: 'pkcs8',
57
+ });
58
+ }
59
+ catch (error) {
60
+ log.error('Failed to create Ed25519 signature:', error);
61
+ const message = error instanceof Error ? error.message : String(error);
62
+ throw new CryptographyError(`Failed to create Ed25519 signature: ${message}`);
63
+ }
64
+ }
65
+ /**
66
+ * Extracts the raw 32-byte public key from DER-encoded SPKI format
67
+ * @param publicKeyDer - DER-encoded public key
68
+ * @returns 32-byte public key buffer
69
+ * @throws CryptographyError if extraction fails
70
+ */
71
+ function extractEd25519PublicKey(publicKeyDer) {
72
+ if (publicKeyDer.length < ED25519_PUBLIC_KEY_LENGTH) {
73
+ throw new CryptographyError('Invalid public key DER format');
74
+ }
75
+ return publicKeyDer.subarray(publicKeyDer.length - ED25519_PUBLIC_KEY_LENGTH);
76
+ }
77
+ /**
78
+ * Extracts the raw 32-byte private key from DER-encoded PKCS#8 format
79
+ * @param privateKeyDer - DER-encoded private key
80
+ * @returns 32-byte private key buffer
81
+ * @throws CryptographyError if extraction fails
82
+ */
83
+ function extractEd25519PrivateKey(privateKeyDer) {
84
+ const octetStringPattern = Buffer.from([0x04, 0x20]);
85
+ const index = privateKeyDer.indexOf(octetStringPattern);
86
+ if (index !== -1 && index + 34 <= privateKeyDer.length) {
87
+ return privateKeyDer.subarray(index + 2, index + 34);
88
+ }
89
+ if (privateKeyDer.length >= 48) {
90
+ return privateKeyDer.subarray(16, 48);
91
+ }
92
+ throw new CryptographyError('Unable to extract private key from DER format');
93
+ }
@@ -0,0 +1,18 @@
1
+ export interface HKDFParams {
2
+ ikm: Buffer;
3
+ salt: Buffer | null;
4
+ info: Buffer;
5
+ length: number;
6
+ }
7
+ /**
8
+ * HMAC-based Key Derivation Function (HKDF) as defined in RFC 5869
9
+ * Derives cryptographic keys from input key material using a two-step process:
10
+ * 1. Extract: Generate a pseudorandom key from the input key material
11
+ * 2. Expand: Expand the pseudorandom key to the desired output length
12
+ *
13
+ * @param params - HKDF parameters including input key material, salt, info, and desired output length
14
+ * @returns Buffer containing the derived key material of specified length
15
+ * @throws CryptographyError if derivation fails or parameters are invalid
16
+ */
17
+ export declare function hkdf(params: HKDFParams): Buffer;
18
+ //# sourceMappingURL=hkdf.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"hkdf.d.ts","sourceRoot":"","sources":["../../../../../src/lib/apple-tv/encryption/hkdf.ts"],"names":[],"mappings":"AAQA,MAAM,WAAW,UAAU;IACzB,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IACpB,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;CAChB;AAID;;;;;;;;;GASG;AACH,wBAAgB,IAAI,CAAC,MAAM,EAAE,UAAU,GAAG,MAAM,CA6B/C"}
@@ -0,0 +1,73 @@
1
+ import { logger } from '@appium/support';
2
+ import { createHmac } from 'node:crypto';
3
+ import { HKDF_HASH_ALGORITHM, HKDF_HASH_LENGTH } from '../constants.js';
4
+ import { CryptographyError } from '../errors.js';
5
+ const log = logger.getLogger('HKDF');
6
+ const MAX_OUTPUT_LENGTH = 255 * HKDF_HASH_LENGTH;
7
+ /**
8
+ * HMAC-based Key Derivation Function (HKDF) as defined in RFC 5869
9
+ * Derives cryptographic keys from input key material using a two-step process:
10
+ * 1. Extract: Generate a pseudorandom key from the input key material
11
+ * 2. Expand: Expand the pseudorandom key to the desired output length
12
+ *
13
+ * @param params - HKDF parameters including input key material, salt, info, and desired output length
14
+ * @returns Buffer containing the derived key material of specified length
15
+ * @throws CryptographyError if derivation fails or parameters are invalid
16
+ */
17
+ export function hkdf(params) {
18
+ const { ikm, salt, info, length } = params;
19
+ if (!ikm || ikm.length === 0) {
20
+ throw new CryptographyError('Input key material (IKM) cannot be empty');
21
+ }
22
+ if (!info) {
23
+ throw new CryptographyError('Info parameter is required');
24
+ }
25
+ if (length <= 0) {
26
+ throw new CryptographyError('Output length must be positive');
27
+ }
28
+ if (length > MAX_OUTPUT_LENGTH) {
29
+ throw new CryptographyError(`Output length cannot exceed ${MAX_OUTPUT_LENGTH} bytes`);
30
+ }
31
+ try {
32
+ const extractedKey = hkdfExtract(ikm, salt);
33
+ return hkdfExpand(extractedKey, info, length);
34
+ }
35
+ catch (error) {
36
+ log.error('HKDF derivation failed:', error);
37
+ const message = error instanceof Error ? error.message : String(error);
38
+ throw new CryptographyError(`HKDF derivation failed: ${message}`);
39
+ }
40
+ }
41
+ /**
42
+ * HKDF Extract step: generates a pseudorandom key from input key material
43
+ * @param ikm - Input key material
44
+ * @param salt - Optional salt value (uses zero salt if null)
45
+ * @returns Pseudorandom key of hash length
46
+ */
47
+ function hkdfExtract(ikm, salt) {
48
+ const actualSalt = salt || Buffer.alloc(HKDF_HASH_LENGTH);
49
+ return createHmac(HKDF_HASH_ALGORITHM, actualSalt).update(ikm).digest();
50
+ }
51
+ /**
52
+ * HKDF Expand a step: expands a pseudorandom key to desired output length
53
+ * @param prk - Pseudorandom key from extract step
54
+ * @param info - Context and application specific information
55
+ * @param length - Desired output key material length
56
+ * @returns Output key material of specified length
57
+ */
58
+ function hkdfExpand(prk, info, length) {
59
+ const numberOfBlocks = Math.ceil(length / HKDF_HASH_LENGTH);
60
+ const blocks = [];
61
+ let previousBlock = Buffer.alloc(0);
62
+ for (let blockIndex = 1; blockIndex <= numberOfBlocks; blockIndex++) {
63
+ const hmac = createHmac(HKDF_HASH_ALGORITHM, prk);
64
+ hmac.update(previousBlock);
65
+ hmac.update(info);
66
+ hmac.update(Buffer.from([blockIndex]));
67
+ const currentBlock = hmac.digest();
68
+ blocks.push(currentBlock);
69
+ previousBlock = currentBlock;
70
+ }
71
+ const outputKeyMaterial = Buffer.concat(blocks);
72
+ return outputKeyMaterial.subarray(0, length);
73
+ }
@@ -0,0 +1,5 @@
1
+ export { Opack2 } from './opack2.js';
2
+ export { encryptChaCha20Poly1305, decryptChaCha20Poly1305, type ChaCha20Poly1305Params, } from './chacha20-poly1305.js';
3
+ export { generateEd25519KeyPair, createEd25519Signature } from './ed25519.js';
4
+ export { hkdf, type HKDFParams } from './hkdf.js';
5
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../src/lib/apple-tv/encryption/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAErC,OAAO,EACL,uBAAuB,EACvB,uBAAuB,EACvB,KAAK,sBAAsB,GAC5B,MAAM,wBAAwB,CAAC;AAEhC,OAAO,EAAE,sBAAsB,EAAE,sBAAsB,EAAE,MAAM,cAAc,CAAC;AAE9E,OAAO,EAAE,IAAI,EAAE,KAAK,UAAU,EAAE,MAAM,WAAW,CAAC"}
@@ -0,0 +1,4 @@
1
+ export { Opack2 } from './opack2.js';
2
+ export { encryptChaCha20Poly1305, decryptChaCha20Poly1305, } from './chacha20-poly1305.js';
3
+ export { generateEd25519KeyPair, createEd25519Signature } from './ed25519.js';
4
+ export { hkdf } from './hkdf.js';
@@ -0,0 +1,57 @@
1
+ interface SerializableArray extends Array<SerializableValue> {
2
+ }
3
+ interface SerializableObject extends Record<string, SerializableValue> {
4
+ }
5
+ type SerializableValue = null | undefined | boolean | number | string | Buffer | SerializableArray | SerializableObject;
6
+ /**
7
+ * OPACK2 binary serialization format encoder
8
+ * Implements Apple's OPACK2 protocol for efficient binary serialization of structured data
9
+ */
10
+ export declare class Opack2 {
11
+ /**
12
+ * Serializes a JavaScript object to OPACK2 binary format
13
+ * @param obj - The object to serialize (supports primitives, arrays, objects, and Buffers)
14
+ * @returns Buffer containing the serialized data
15
+ * @throws AppleTVError if the object contains unsupported types
16
+ */
17
+ static dumps(obj: SerializableValue): Buffer;
18
+ /**
19
+ * Main encoding dispatcher that routes values to appropriate type-specific encoders
20
+ * @param obj - Value to encode
21
+ * @returns Buffer containing encoded value
22
+ * @throws AppleTVError for unsupported types
23
+ */
24
+ private static encode;
25
+ /**
26
+ * Encodes numeric values with the appropriate size optimization
27
+ * @param num - Number to encode
28
+ * @returns Buffer containing encoded number
29
+ */
30
+ private static encodeNumber;
31
+ /**
32
+ * Encodes UTF-8 strings with length-optimized headers
33
+ * @param str - String to encode
34
+ * @returns Buffer containing encoded string
35
+ */
36
+ private static encodeString;
37
+ /**
38
+ * Encodes binary data with length-optimized headers
39
+ * @param bytes - Buffer to encode
40
+ * @returns Buffer containing encoded binary data
41
+ */
42
+ private static encodeBytes;
43
+ /**
44
+ * Encodes arrays with count-optimized headers
45
+ * @param arr - Array to encode
46
+ * @returns Buffer containing encoded array
47
+ */
48
+ private static encodeArray;
49
+ /**
50
+ * Encodes objects/dictionaries with count-optimized headers
51
+ * @param dict - Object to encode
52
+ * @returns Buffer containing encoded dictionary
53
+ */
54
+ private static encodeDict;
55
+ }
56
+ export {};
57
+ //# sourceMappingURL=opack2.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"opack2.d.ts","sourceRoot":"","sources":["../../../../../src/lib/apple-tv/encryption/opack2.ts"],"names":[],"mappings":"AAGA,UAAU,iBAAkB,SAAQ,KAAK,CAAC,iBAAiB,CAAC;CAAG;AAC/D,UAAU,kBAAmB,SAAQ,MAAM,CAAC,MAAM,EAAE,iBAAiB,CAAC;CAAG;AAEzE,KAAK,iBAAiB,GAClB,IAAI,GACJ,SAAS,GACT,OAAO,GACP,MAAM,GACN,MAAM,GACN,MAAM,GACN,iBAAiB,GACjB,kBAAkB,CAAC;AAEvB;;;GAGG;AACH,qBAAa,MAAM;IACjB;;;;;OAKG;IACH,MAAM,CAAC,KAAK,CAAC,GAAG,EAAE,iBAAiB,GAAG,MAAM;IAI5C;;;;;OAKG;IACH,OAAO,CAAC,MAAM,CAAC,MAAM;IAwCrB;;;;OAIG;IACH,OAAO,CAAC,MAAM,CAAC,YAAY;IAiC3B;;;;OAIG;IACH,OAAO,CAAC,MAAM,CAAC,YAAY;IAqC3B;;;;OAIG;IACH,OAAO,CAAC,MAAM,CAAC,WAAW;IAoC1B;;;;OAIG;IACH,OAAO,CAAC,MAAM,CAAC,WAAW;IAuB1B;;;;OAIG;IACH,OAAO,CAAC,MAAM,CAAC,UAAU;CAyB1B"}