
CMF Watch Pro 2 के लिए रिवर्स-इंजीनियर्ड BLE प्रोटोकॉल, GATT लेआउट, AES-128-CBC एन्क्रिप्टेड कमांड फ्रेम्स, प्रमाणीकरण हैंडशेक और वैकल्पिक साथी ऐप विकास हेतु स्वास्थ्य डेटा सिंक का दस्तावेज़ीकरण।
अनौपचारिक। यह दस्तावेज़ CMF Watch Pro 2 (CMF by Nothing) के ब्लूटूथ लो एनर्जी (BLE) प्रोटोकॉल का वर्णन करता है, जिसे एक वैकल्पिक साथी ऐप के लिए रिवर्स इंजीनियरिंग द्वारा पुनर्निर्मित किया गया है। यह Nothing/CMF से संबद्ध या समर्थित नहीं है। अपने जोखिम पर उपयोग करें।
फ्रेम हेडर और ऑपकोड में सभी मल्टी-बाइट पूर्णांक बिग-एंडियन हैं। कमांड पेलोड के अंदर पूर्णांक लिटिल-एंडियन हैं जब तक कि अन्यथा न कहा गया हो (यह डिवाइस फर्मवेयर को दर्शाता है) — अपवादों पर ध्यान दें (GOALS_SET, GPS_PUSH, बल्क-ट्रांसफर ऑफसेट/लंबाई बिग-एंडियन हैं)।
नीचे प्रत्येक गैर-स्पष्ट दावे को टैग किया गया है कि इसे कैसे स्थापित किया गया था:
जहाँ बाद का अनुभाग पहले वाले को सुधारता है, वहाँ पहले वाला पाठ हटाने के बजाय एक पॉइंटर के साथ रखा जाता है — यह जानना कि कौन से रीडिंग आज़माए गए और खारिज किए गए, अगले व्यक्ति को वही चक्कर बचाता है।
सभी कैप्चर के लिए परीक्षण डिवाइस: CMF Watch Pro 2-5485, fw 1.0.0.73, सीरियल CI04102520008192, MCU Actions ATS3089C (Cortex-M4), स्क्रीन 466×360।
फ़ोन GATT क्लाइंट है; घड़ी पेरिफेरल है, जो CMF Watch Pro 2-XXXX (4 हेक्स वर्ण) के रूप में विज्ञापन करती है।
| उद्देश्य | सेवा | विशेषता | गुण |
|---|---|---|---|
| कमांड लिखें | 0000fff0-0000-1000-8000-00805f9b34fb | 0000fff2-… | Write |
| कमांड सूचित करें | 0000fff0-… | 0000fff1-… | Notify |
| शेल लिखें (AT) | — | 77d4ff01-2fe2-2334-0d35-9ccd078f529c | Write |
| शेल सूचित करें (AT) | — | 77d4ff02-… | Notify |
| बल्क डेटा लिखें | — | 02f00000-0000-0000-0000-00000000ffe1 | Write |
| बल्क डेटा सूचित करें | — | 02f00000-…ffe2 | Notify |
प्रत्येक CCCD (00002902-…) पर 01 00 लिखकर नोटिफिकेशन सक्षम करें। कमांड चैनल (fff1/fff2) नीचे दिए गए फ्रेम किए गए प्रोटोकॉल को ले जाता है। शेल चैनल (77d4…) सादा AT-शैली टेक्स्ट ले जाता है (जैसे AT GETSECRET; §14 देखें)। डेटा चैनल (02f0…) बड़े बाइनरी ब्लॉब्स (वॉचफेस, फर्मवेयर, AGPS) ले जाता है, जो कमांड चैनल पर नियंत्रण ऑपकोड द्वारा समन्वित होते हैं।
सेवा UUID — घड़ी ~10 प्राथमिक सेवाओं का विज्ञापन करती है। वास्तविक इकाई पर गणना की गई:
0xfff0 (कमांड), 0x180f (बैटरी), 0x180a (डिवाइस जानकारी), 0xefe7, 0xffd0,
02f00000-…ffe0 और 02f00000-…fe00 (डेटा), 77d4e67c-2fe2-2334-0d35-9ccd078f529c (शेल / पेयरिंग), e49a3001-f69a-11e8-8eb2-f2801f1b9fd1, f48a23c0-f69a-11e8-8eb2-f2801f1b9fd1।
⚠️ शेल सेवा UUID
77d4e67c-…है, न कि77d4ff00-…। इस दस्तावेज़ के पहले के संशोधनों ने मान लिया था कि सेवा अपनी विशेषताओं (77d4ff01/77d4ff02, §14) केff00उपसर्ग को साझा करती है — ऐसा नहीं है, कम से कम उस इकाई पर जिस पर यह जाँचा गया था (खोज freethinkel/fmc से, §स्रोत देखें)। विशेषता UUID अपरिवर्तित हैं। यह सत्यापित नहीं है कि77d4e67cइकाइयों में स्थिर है या नहीं — हार्डकोड करने के बजाय गणना करें।
🌐 वेब ब्लूटूथ कॉलआउट। क्रोमियम केवल उन सेवाओं को खोजता है जिन्हें पृष्ठ ने
optionalServicesमें सूचीबद्ध किया है, यहाँ तक कि एक अनफ़िल्टर्डgetPrimaryServices()कॉल के लिए भी — 3 सेवाओं को सूचीबद्ध करने वाला पृष्ठ 3 देखता है, जबकिchrome://bluetooth-internals(क्रोम का अपना C++ लेयर, अनस्कोप्ड) सभी 10 दिखाता है। यदि आप एक ब्राउज़र क्लाइंट लिखते हैं, तो ऊपर हर UUID को पहले से सूचीबद्ध करें या पेयरिंग उन सेवाओं के साथ विफल हो जाएगी जो स्पष्ट रूप से मौजूद हैं। फ़ायरफ़ॉक्स/सफारी में कोई वेब ब्लूटूथ नहीं; उपयोगकर्ता इशारा + HTTPS/localhost की आवश्यकता है।
✅ एक पूरा वास्तविक सत्र एकल कमांड चैनल पर चला — 160 s के भारी-उपयोग कैप्चर के दौरान डेटा/फर्मवेयर या शेल चैनलों पर कोई ट्रैफ़िक नहीं था, सिवाय एक स्पष्ट OTA/वॉचफेस ट्रांसफर के।
0xF5)प्रत्येक कमांड-चैनल संदेश एक या अधिक 11-बाइट-हेडर फ्रेम में लपेटा जाता है:``` +------+-----------+--------+-------------+-------------+--------+-------------------+ | 0xF5 | chunkLen | cmd1 | chunkCount | chunkIndex | cmd2 | chunk bytes … | | 1 B | 2 B (BE) | 2 B BE | 2 B BE | 2 B BE | 2 B BE | chunkLen bytes | +------+-----------+--------+-------------+-------------+--------+-------------------+ __________________________ 11-byte header ____________________________/
- `cmd1`/`cmd2` मिलकर **opcode** बनाते हैं (§6 देखें)। 🔎 आधिकारिक ऐप के फ्रेम बिल्डर (`C6117b.m30831g`) के विरुद्ध पुष्टि की गई।
- `chunkCount` = इस कमांड के लिए कुल चंक; `chunkIndex` **1-आधारित** है।
- `chunkLen` = इस फ्रेम में `chunk` के बाइट्स की संख्या।
- एक एकल BLE राइट लिंक MTU द्वारा खंडित हो सकती है; रिसीवर कच्चे बाइट्स को बफर करता है और पूर्ण फ्रेम्स को फिर से निकालता है। बड़े पेलोड को कई चंक्स में विभाजित किया जाता है (समान `cmd1/cmd2`, बढ़ता हुआ `chunkIndex`) और क्रम में पुनः जोड़ा जाता है।
### Opcode परंपरा (✅ वायर पर पुष्टि की गई)
- `cmd1 = 0xFFFF`: `cmd2` में `0x80xx`/`0x90xx` = फोन→घड़ी (अनुरोध/सेट); `0x00xx`/`0xa0xx` = घड़ी→फोन (उत्तर)। जोड़े निम्न बाइट द्वारा मेल खाते हैं (`0x9055`↔`0xa055`, `0x8051`↔`0x0051`)।
- फीचर-विशिष्ट `cmd1`: `cmd2` प्रत्यय = `0x0001` **SET**, `0x0002` **GET**, `0x0003` **ACK**।
### चंक बॉडी
प्रत्येक चंक के लिए, बॉडी है `payloadPiece ‖ CRC32_LE(payloadPiece)` (4-बाइट CRC, लिटिल-एंडियन, zlib/IEEE)। यदि कमांड **एन्क्रिप्टेड** है (§3 देखें), तो पूरा `payloadPiece ‖ CRC` फिर AES-128-CBC/PKCS7 से एन्क्रिप्ट किया जाता है और वह सिफरटेक्स्ट फ्रेम का `chunk` बन जाता है।
**प्लेनटेक्स्ट विचित्रता:** प्लेनटेक्स्ट opcodes के लिए घड़ी `chunkLen` में 4-बाइट CRC को *गिनती* है लेकिन उसे **प्रसारित** नहीं करती। इसलिए प्लेनटेक्स्ट फ्रेम को डिकोड करते समय, वास्तविक डेटा लंबाई `chunkLen − 4` होती है। (एन्क्रिप्टेड फ्रेम्स CRC को सिफरटेक्स्ट के अंदर सामान्य रूप से ले जाते हैं।)
चंक आकार (ताकि एन्क्रिप्टेड चंक्स AES ब्लॉक सीमाओं पर पहुँचें), `maxWrite = mtu − 3` के साथ:
- एन्क्रिप्टेड: `floor((maxWrite − 11) / 16) * 16 − 4 − 1`
- प्लेनटेक्स्ट: `maxWrite − 11 − 4 − 1`
✅ सभी देखे गए एन्क्रिप्टेड फ्रेम `chunkLen` मान 16 के गुणक थे (ब्लॉक संरेखण बना रहता है)।
---
## 3. क्रिप्टोग्राफिक प्रिमिटिव्स
- **AES-128-CBC** जिसमें **PKCS7** पैडिंग और एक **निश्चित IV** (फर्मवेयर `CmfCharacteristic.AES_IV` से):
`50 51 52 53 54 55 56 57 60 61 62 63 64 65 66 5A`।
- **CRC32** (zlib/IEEE), 4 लिटिल-एंडियन बाइट्स के रूप में उत्सर्जित।
- **SHA-256** भागों के संयोजन पर।
कुंजी व्युत्पत्ति:```
authkey = SHA256( rnd1 ‖ rnd2 ‖ secret )[0..16] // persisted across sessions
sessionKey = SHA256( nonce ‖ authkey )[0..16] // per connection
secret = 16-बाइट डिवाइस सीक्रेट (वॉच से शेल कमांड AT GETSECRET → GETSECRET:<32-hex>,OK के माध्यम से प्राप्त किया जा सकता है)।rnd1 = फोन द्वारा चुने गए 16 रैंडम बाइट्स; rnd2 = वॉच से 16 रैंडम बाइट्स।nonce = वॉच के nonce रिप्लाई से बाइट्स।कुंजी सेट होने के बाद, सभी कमांड-चैनल फ्रेम AES-एन्क्रिप्टेड होते हैं सिवाय §5 में सूचीबद्ध प्लेनटेक्स्ट ऑपकोड्स के।
✅ दोनों डेरिवेशन मान्य किए गए: रूट किए गए फोन के ntwatch.db से प्राप्त authkey कैप्चर किए गए rnd1/rnd2/secret से व्युत्पन्न मान से मेल खाता था; कैप्चर किए गए nonce से पुनरुत्पादित sessionKey लाइव फ्रेम को डिक्रिप्ट करता है।
दो एंट्री पथ एक ही nonce/confirm टेल साझा करते हैं।