(base64: string)
| 19 | `; |
| 20 | |
| 21 | export function getTelegramBackupIPFromBase64(base64: string) { |
| 22 | // 1. Check base64 size |
| 23 | if (base64.length !== 344) { |
| 24 | throw new TypeError('Invalid base64 length'); |
| 25 | } |
| 26 | |
| 27 | // 2. Filter to base64 and check length |
| 28 | // Not needed with base64ToUint8Array, it has built-in base64-able checking |
| 29 | |
| 30 | // 3. Decode base64 to Buffer |
| 31 | const decoded = base64ToUint8Array(base64); |
| 32 | if (decoded.length !== 256) { |
| 33 | throw new TypeError('Decoded buffer length is not 344 bytes, received ' + decoded.length); |
| 34 | } |
| 35 | |
| 36 | // 4. RSA decrypt (public key, "decrypt signature" - usually means "verify and extract") |
| 37 | // In Node.js, publicDecrypt is used for signature verification (Note that no padding is needed) |
| 38 | const publicKey = crypto.createPublicKey(mtptoto_public_rsa); |
| 39 | const decrypted = crypto.publicDecrypt( |
| 40 | { |
| 41 | key: publicKey, |
| 42 | padding: crypto.constants.RSA_NO_PADDING |
| 43 | }, |
| 44 | decoded |
| 45 | ); |
| 46 | |
| 47 | // 5. Extract AES key/iv and encrypted payload |
| 48 | const key = decrypted.subarray(0, 32); |
| 49 | const iv = decrypted.subarray(16, 32); |
| 50 | const dataCbc = decrypted.subarray(32); // 224 bytes |
| 51 | |
| 52 | if (dataCbc.length !== 224) { |
| 53 | throw new Error(`Invalid AES payload length: ${dataCbc.length}`); |
| 54 | } |
| 55 | |
| 56 | // 6. AES-CBC decrypt |
| 57 | const decipher = crypto.createDecipheriv('aes-256-cbc', key, iv); |
| 58 | decipher.setAutoPadding(false); |
| 59 | |
| 60 | const decryptedCbc = concatUint8Arrays([decipher.update(dataCbc), decipher.final()]); |
| 61 | |
| 62 | if (decryptedCbc.length !== 224) { |
| 63 | throw new Error(`Decrypted AES payload length is not 224 bytes, received ${decryptedCbc.length}`); |
| 64 | } |
| 65 | |
| 66 | // 7. SHA256 check |
| 67 | const currentHash = crypto |
| 68 | .createHash('sha256') |
| 69 | .update(decryptedCbc.subarray(0, 208)) |
| 70 | .digest() |
| 71 | .subarray(0, 16); |
| 72 | |
| 73 | const expectedHash = decryptedCbc.subarray(208, 224); |
| 74 | // check if hash matches |
| 75 | if (!currentHash.equals(expectedHash)) { |
| 76 | throw new Error('SHA256 hash mismatch'); |
| 77 | } |
| 78 |
no outgoing calls
no test coverage detected