UNPKG

meshcore-decoder

Version:

TypeScript library for decoding MeshCore packets

288 lines (227 loc) 11.5 kB
# MeshCore Decoder A TypeScript library for decoding MeshCore mesh networking packets with full cryptographic support. ## Features - **Packet Decoding**: Decode MeshCore packets - **Built-in Decryption**: Decrypt GroupText, TextMessage, and other encrypted payloads - **Developer Friendly**: TypeScript-first with full type safety and portability of JavaScript ## Installation ```bash npm install meshcore-decoder ``` ## Quick Start ```typescript import { MeshCoreDecoder, PayloadType, Utils, DecodedPacket, AdvertPayload } from 'meshcore-decoder'; // Decode a MeshCore packet const hexData: string = '11007E7662676F7F0850A8A355BAAFBFC1EB7B4174C340442D7D7161C9474A2C94006CE7CF682E58408DD8FCC51906ECA98EBF94A037886BDADE7ECD09FD92B839491DF3809C9454F5286D1D3370AC31A34593D569E9A042A3B41FD331DFFB7E18599CE1E60992A076D50238C5B8F85757375354522F50756765744D65736820436F75676172'; const packet: DecodedPacket = MeshCoreDecoder.decode(hexData); console.log(`Route Type: ${Utils.getRouteTypeName(packet.routeType)}`); console.log(`Payload Type: ${Utils.getPayloadTypeName(packet.payloadType)}`); console.log(`Message Hash: ${packet.messageHash}`); if (packet.payloadType === PayloadType.Advert && packet.payload.decoded) { const advert: AdvertPayload = packet.payload.decoded as AdvertPayload; console.log(`Device Name: ${advert.appData.name}`); console.log(`Device Role: ${Utils.getDeviceRoleName(advert.appData.deviceRole)}`); if (advert.appData.location) { console.log(`Location: ${advert.appData.location.latitude}, ${advert.appData.location.longitude}`); } } ``` ## Full Packet Structure Example Here's what a complete decoded packet looks like: ```typescript import { MeshCoreDecoder, DecodedPacket } from 'meshcore-decoder'; const hexData: string = '11007E7662676F7F0850A8A355BAAFBFC1EB7B4174C340442D7D7161C9474A2C94006CE7CF682E58408DD8FCC51906ECA98EBF94A037886BDADE7ECD09FD92B839491DF3809C9454F5286D1D3370AC31A34593D569E9A042A3B41FD331DFFB7E18599CE1E60992A076D50238C5B8F85757375354522F50756765744D65736820436F75676172'; const packet: DecodedPacket = MeshCoreDecoder.decode(hexData); console.log(JSON.stringify(packet, null, 2)); ``` **Output:** ```json { "messageHash": "F9C060FE", "routeType": 1, "payloadType": 4, "payloadVersion": 0, "pathLength": 0, "path": null, "payload": { "raw": "7E7662676F7F0850A8A355BAAFBFC1EB7B4174C340442D7D7161C9474A2C94006CE7CF682E58408DD8FCC51906ECA98EBF94A037886BDADE7ECD09FD92B839491DF3809C9454F5286D1D3370AC31A34593D569E9A042A3B41FD331DFFB7E18599CE1E60992A076D50238C5B8F85757375354522F50756765744D65736820436F75676172", "decoded": { "type": 4, "version": 0, "isValid": true, "publicKey": "7E7662676F7F0850A8A355BAAFBFC1EB7B4174C340442D7D7161C9474A2C9400", "timestamp": 1758455660, "signature": "2E58408DD8FCC51906ECA98EBF94A037886BDADE7ECD09FD92B839491DF3809C9454F5286D1D3370AC31A34593D569E9A042A3B41FD331DFFB7E18599CE1E609", "appData": { "flags": 146, "deviceRole": 2, "hasLocation": true, "hasName": true, "location": { "latitude": 47.543968, "longitude": -122.108616 }, "name": "WW7STR/PugetMesh Cougar" } } }, "totalBytes": 134, "isValid": true } ``` ## Packet Support | Value | Name | Description | Decoding | Decryption | Segment Analysis | |-------|------|-------------|----------|------------|------------------| | `0x00` | Request | Request (destination/source hashes + MAC) | | 🚧 | | | `0x01` | Response | Response to REQ or ANON_REQ | | 🚧 | | | `0x02` | Plain text message | Plain text message | | 🚧 | | | `0x03` | Acknowledgment | Acknowledgment | | N/A | | | `0x04` | Node advertisement | Node advertisement | | N/A | | | `0x05` | Group text message | Group text message | | | | | `0x06` | Group datagram | Group datagram | 🚧 | 🚧 | 🚧 | | `0x07` | Anonymous request | Anonymous request | | 🚧 | | | `0x08` | Returned path | Returned path | | N/A | | | `0x09` | Trace | Trace a path, collecting SNI for each hop | | N/A | | | `0x0A` | Multi-part packet | Packet is part of a sequence of packets | 🚧 | 🚧 | 🚧 | | `0x0F` | Custom packet | Custom packet (raw bytes, custom encryption) | 🚧 | 🚧 | 🚧 | **Legend:** - Fully implemented - 🚧 Planned/In development - `-` Not applicable For some packet types not yet supported here, they may not exist in MeshCore yet or I have yet to observe these packet types on the mesh. ## Decryption Support Simply provide your channel secret keys and the library handles everything else: ```typescript import { MeshCoreDecoder, PayloadType, CryptoKeyStore, DecodedPacket, GroupTextPayload } from 'meshcore-decoder'; // Create a key store with channel secret keys const keyStore: CryptoKeyStore = MeshCoreDecoder.createKeyStore({ channelSecrets: [ '8b3387e9c5cdea6ac9e5edbaa115cd72', // Public channel (channel hash 11) 'ff2b7d74e8d20f71505bda9ea8d59a1c', // A different channel's secret ] }); const groupTextHexData: string = '...'; // Your encrypted GroupText packet hex // Decode encrypted GroupText message const encryptedPacket: DecodedPacket = MeshCoreDecoder.decode(groupTextHexData, { keyStore }); if (encryptedPacket.payloadType === PayloadType.GroupText && encryptedPacket.payload.decoded) { const groupText: GroupTextPayload = encryptedPacket.payload.decoded as GroupTextPayload; if (groupText.decrypted) { console.log(`Sender: ${groupText.decrypted.sender}`); console.log(`Message: ${groupText.decrypted.message}`); console.log(`Timestamp: ${new Date(groupText.decrypted.timestamp * 1000).toISOString()}`); } else { console.log('Message encrypted (no key available)'); } } ``` The library automatically: - Calculates channel hashes from your secret keys using SHA256 - Handles hash collisions (multiple keys with same first byte) by trying all matching keys - Verifies message authenticity using HMAC-SHA256 - Decrypts using AES-128 ECB ## Packet Structure Analysis For detailed packet analysis and debugging, use `analyzeStructure()` to get byte-level breakdowns: ```typescript import { MeshCoreDecoder, PacketStructure } from 'meshcore-decoder'; console.log('=== Packet Breakdown ==='); const hexData: string = '11007E7662676F7F0850A8A355BAAFBFC1EB7B4174C340442D7D7161C9474A2C94006CE7CF682E58408DD8FCC51906ECA98EBF94A037886BDADE7ECD09FD92B839491DF3809C9454F5286D1D3370AC31A34593D569E9A042A3B41FD331DFFB7E18599CE1E60992A076D50238C5B8F85757375354522F50756765744D65736820436F75676172'; console.log('Packet length:', hexData.length); console.log('Expected bytes:', hexData.length / 2); const structure: PacketStructure = MeshCoreDecoder.analyzeStructure(hexData); console.log('\nMain segments:'); structure.segments.forEach((seg, i) => { console.log(`${i+1}. ${seg.name} (bytes ${seg.startByte}-${seg.endByte}): ${seg.value}`); }); console.log('\nPayload segments:'); structure.payload.segments.forEach((seg, i) => { console.log(`${i+1}. ${seg.name} (bytes ${seg.startByte}-${seg.endByte}): ${seg.value}`); console.log(` Description: ${seg.description}`); }); ``` **Output:** ``` === Packet Breakdown === Packet length: 268 Expected bytes: 134 Main segments: 1. Header (bytes 0-0): 0x11 2. Path Length (bytes 1-1): 0x00 3. Payload (bytes 2-133): 7E7662676F7F0850A8A355BAAFBFC1EB7B4174C340442D7D7161C9474A2C94006CE7CF682E58408DD8FCC51906ECA98EBF94A037886BDADE7ECD09FD92B839491DF3809C9454F5286D1D3370AC31A34593D569E9A042A3B41FD331DFFB7E18599CE1E60992A076D50238C5B8F85757375354522F50756765744D65736820436F75676172 Payload segments: 1. Public Key (bytes 0-31): 7E7662676F7F0850A8A355BAAFBFC1EB7B4174C340442D7D7161C9474A2C9400 Description: Ed25519 public key 2. Timestamp (bytes 32-35): 6CE7CF68 Description: 1758455660 (2025-09-21T11:54:20Z) 3. Signature (bytes 36-99): 2E58408DD8FCC51906ECA98EBF94A037886BDADE7ECD09FD92B839491DF3809C9454F5286D1D3370AC31A34593D569E9A042A3B41FD331DFFB7E18599CE1E609 Description: Ed25519 signature 4. App Flags (bytes 100-100): 92 Description: Binary: 10010010 | Bits 0-3 (Role): Room server | Bit 4 (Location): Yes | Bit 5 (Feature1): No | Bit 6 (Feature2): No | Bit 7 (Name): Yes 5. Latitude (bytes 101-104): A076D502 Description: 47.543968° (47.543968) 6. Longitude (bytes 105-108): 38C5B8F8 Description: -122.108616° (-122.108616) 7. Node Name (bytes 109-131): 5757375354522F50756765744D65736820436F75676172 Description: Node name: "WW7STR/PugetMesh Cougar" ``` The `analyzeStructure()` method provides: - **Header breakdown** with bit-level field analysis - **Byte-accurate segments** with start/end positions - **Payload field parsing** for all supported packet types - **Human-readable descriptions** for each field ### Command Line Interface For quick analysis from the terminal, install globally and use the CLI: ```bash # Install globally npm install -g meshcore-decoder # Analyze a packet meshcore-decoder 11007E7662676F7F0850A8A355BAAFBFC1EB7B4174C340442D7D7161C9474A2C94006CE7CF682E58408DD8FCC51906ECA98EBF94A037886BDADE7ECD09FD92B839491DF3809C9454F5286D1D3370AC31A34593D569E9A042A3B41FD331DFFB7E18599CE1E60992A076D50238C5B8F85757375354522F50756765744D65736820436F75676172 # With decryption (provide channel secrets) meshcore-decoder 150011C3C1354D619BAE9590E4D177DB7EEAF982F5BDCF78005D75157D9535FA90178F785D --key 8b3387e9c5cdea6ac9e5edbaa115cd72 # Show detailed structure analysis meshcore-decoder --structure 11007E7662676F7F0850A8A355BAAFBFC1EB7B4174C340442D7D7161C9474A2C94006CE7CF682E58408DD8FCC51906ECA98EBF94A037886BDADE7ECD09FD92B839491DF3809C9454F5286D1D3370AC31A34593D569E9A042A3B41FD331DFFB7E18599CE1E60992A076D50238C5B8F85757375354522F50756765744D65736820436F75676172 # JSON output meshcore-decoder --json 11007E7662676F7F0850A8A355BAAFBFC1EB7B4174C340442D7D7161C9474A2C94006CE7CF682E58408DD8FCC51906ECA98EBF94A037886BDADE7ECD09FD92B839491DF3809C9454F5286D1D3370AC31A34593D569E9A042A3B41FD331DFFB7E18599CE1E60992A076D50238C5B8F85757375354522F50756765744D65736820436F75676172 ``` ## Development ```bash # Install dependencies npm install # Run tests npm test # Run tests in watch mode npm run test:watch # Build for production npm run build # Development with ts-node npm run dev ``` ## License MIT License Copyright (c) 2025 Michael Hart <michaelhart@michaelhart.me> (https://github.com/michaelhart) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.