mirror of
https://github.com/jkingsman/Remote-Terminal-for-MeshCore.git
synced 2026-03-28 17:43:05 +01:00
Fix multibyte meshcore-decoder dep hell
This commit is contained in:
@@ -5,11 +5,8 @@ ARG COMMIT_HASH=unknown
|
||||
|
||||
WORKDIR /build
|
||||
|
||||
RUN apt-get update \
|
||||
&& apt-get install -y --no-install-recommends git \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
|
||||
COPY frontend/package.json ./
|
||||
COPY frontend/package.json frontend/.npmrc ./
|
||||
COPY frontend/lib/meshcore-decoder ./lib/meshcore-decoder
|
||||
RUN npm install
|
||||
|
||||
COPY frontend/ ./
|
||||
|
||||
@@ -1141,7 +1141,7 @@ SOFTWARE.
|
||||
|
||||
</details>
|
||||
|
||||
### meshcore-hashtag-cracker (1.7.0) — MIT
|
||||
### meshcore-hashtag-cracker (1.10.0) — MIT
|
||||
|
||||
<details>
|
||||
<summary>Full license text</summary>
|
||||
|
||||
1
frontend/.npmrc
Normal file
1
frontend/.npmrc
Normal file
@@ -0,0 +1 @@
|
||||
install-links=true
|
||||
@@ -12,6 +12,7 @@ Keep it aligned with `frontend/src` source code.
|
||||
- Tailwind utility classes + local CSS (`index.css`, `styles.css`)
|
||||
- Sonner (toasts)
|
||||
- Leaflet / react-leaflet (map)
|
||||
- Vendored `@michaelhart/meshcore-decoder` in `frontend/lib/meshcore-decoder` (local file dependency for multibyte-support build)
|
||||
- `meshcore-hashtag-cracker` + `nosleep.js` (channel cracker)
|
||||
|
||||
## Frontend Map
|
||||
@@ -138,6 +139,9 @@ frontend/src/
|
||||
├── useContactsAndChannels.test.ts
|
||||
├── useWebSocket.dispatch.test.ts
|
||||
└── useWebSocket.lifecycle.test.ts
|
||||
|
||||
frontend/lib/
|
||||
└── meshcore-decoder/ # Vendored local decoder package used by app + hashtag cracker
|
||||
```
|
||||
|
||||
## Architecture Notes
|
||||
|
||||
21
frontend/lib/meshcore-decoder/LICENSE.md
Normal file
21
frontend/lib/meshcore-decoder/LICENSE.md
Normal file
@@ -0,0 +1,21 @@
|
||||
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.
|
||||
453
frontend/lib/meshcore-decoder/README.md
Normal file
453
frontend/lib/meshcore-decoder/README.md
Normal file
@@ -0,0 +1,453 @@
|
||||
# MeshCore Decoder
|
||||
|
||||
A TypeScript library for decoding MeshCore mesh networking packets with full cryptographic support. Uses WebAssembly (WASM) for Ed25519 key derivation through the [orlp/ed25519 library](https://github.com/orlp/ed25519).
|
||||
|
||||
This powers the [MeshCore Packet Analyzer](https://analyzer.letsme.sh/).
|
||||
|
||||
## 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
|
||||
|
||||
### Install to a single project
|
||||
|
||||
```bash
|
||||
npm install @michaelhart/meshcore-decoder
|
||||
```
|
||||
|
||||
### Install CLI (install globally)
|
||||
|
||||
```bash
|
||||
npm install -g @michaelhart/meshcore-decoder
|
||||
```
|
||||
|
||||
## Quick Start
|
||||
|
||||
```typescript
|
||||
import {
|
||||
MeshCoreDecoder,
|
||||
PayloadType,
|
||||
Utils,
|
||||
DecodedPacket,
|
||||
AdvertPayload
|
||||
} from '@michaelhart/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 '@michaelhart/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 '@michaelhart/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 '@michaelhart/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
|
||||
|
||||
## Ed25519 Key Derivation
|
||||
|
||||
The library includes MeshCore-compatible Ed25519 key derivation using the exact orlp/ed25519 algorithm via WebAssembly:
|
||||
|
||||
```typescript
|
||||
import { Utils } from '@michaelhart/meshcore-decoder';
|
||||
|
||||
// Derive public key from MeshCore private key (64-byte format)
|
||||
const privateKey = '18469d6140447f77de13cd8d761e605431f52269fbff43b0925752ed9e6745435dc6a86d2568af8b70d3365db3f88234760c8ecc645ce469829bc45b65f1d5d5';
|
||||
|
||||
const publicKey = await Utils.derivePublicKey(privateKey);
|
||||
console.log('Derived Public Key:', publicKey);
|
||||
// Output: 4852B69364572B52EFA1B6BB3E6D0ABED4F389A1CBFBB60A9BBA2CCE649CAF0E
|
||||
|
||||
// Validate a key pair
|
||||
const isValid = await Utils.validateKeyPair(privateKey, publicKey);
|
||||
console.log('Key pair valid:', isValid); // true
|
||||
```
|
||||
|
||||
### Command Line Interface
|
||||
|
||||
For quick analysis from the terminal, install globally and use the CLI:
|
||||
|
||||
```bash
|
||||
# Install globally
|
||||
npm install -g @michaelhart/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
|
||||
|
||||
# Derive public key from MeshCore private key
|
||||
meshcore-decoder derive-key 18469d6140447f77de13cd8d761e605431f52269fbff43b0925752ed9e6745435dc6a86d2568af8b70d3365db3f88234760c8ecc645ce469829bc45b65f1d5d5
|
||||
|
||||
# Validate key pair
|
||||
meshcore-decoder derive-key 18469d6140447f77de13cd8d761e605431f52269fbff43b0925752ed9e6745435dc6a86d2568af8b70d3365db3f88234760c8ecc645ce469829bc45b65f1d5d5 --validate 4852b69364572b52efa1b6bb3e6d0abed4f389a1cbfbb60a9bba2cce649caf0e
|
||||
|
||||
# Key derivation with JSON output
|
||||
meshcore-decoder derive-key 18469d6140447f77de13cd8d761e605431f52269fbff43b0925752ed9e6745435dc6a86d2568af8b70d3365db3f88234760c8ecc645ce469829bc45b65f1d5d5 --json
|
||||
```
|
||||
|
||||
|
||||
## Using with Angular
|
||||
|
||||
The library works in Angular (and other browser-based) applications but requires additional configuration for WASM support and browser compatibility.
|
||||
|
||||
### 1. Configure Assets in `angular.json`
|
||||
|
||||
Add the WASM files to your Angular assets configuration:
|
||||
|
||||
```json
|
||||
{
|
||||
"projects": {
|
||||
"your-app": {
|
||||
"architect": {
|
||||
"build": {
|
||||
"options": {
|
||||
"assets": [
|
||||
// ... your existing assets ...
|
||||
{
|
||||
"glob": "orlp-ed25519.*",
|
||||
"input": "./node_modules/@michaelhart/meshcore-decoder/lib",
|
||||
"output": "assets/"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 2. Create a WASM Service
|
||||
|
||||
Create `src/app/services/meshcore-wasm.ts`:
|
||||
|
||||
```typescript
|
||||
import { Injectable } from '@angular/core';
|
||||
import { BehaviorSubject } from 'rxjs';
|
||||
|
||||
@Injectable({ providedIn: 'root' })
|
||||
export class MeshCoreWasmService {
|
||||
private wasm: any = null;
|
||||
public ready = new BehaviorSubject<boolean>(false);
|
||||
|
||||
constructor() {
|
||||
this.loadWasm();
|
||||
}
|
||||
|
||||
private async loadWasm() {
|
||||
try {
|
||||
const jsResponse = await fetch('/assets/orlp-ed25519.js');
|
||||
const jsText = await jsResponse.text();
|
||||
|
||||
const script = document.createElement('script');
|
||||
script.textContent = jsText;
|
||||
document.head.appendChild(script);
|
||||
|
||||
this.wasm = await (window as any).OrlpEd25519({
|
||||
locateFile: (path: string) => path === 'orlp-ed25519.wasm' ? '/assets/orlp-ed25519.wasm' : path
|
||||
});
|
||||
|
||||
this.ready.next(true);
|
||||
} catch (error) {
|
||||
console.error('WASM load failed:', error);
|
||||
this.ready.next(false);
|
||||
}
|
||||
}
|
||||
|
||||
derivePublicKey(privateKeyHex: string): string | null {
|
||||
if (!this.wasm) return null;
|
||||
|
||||
const privateKeyBytes = this.hexToBytes(privateKeyHex);
|
||||
const privateKeyPtr = 1024;
|
||||
const publicKeyPtr = 1088;
|
||||
|
||||
this.wasm.HEAPU8.set(privateKeyBytes, privateKeyPtr);
|
||||
|
||||
const result = this.wasm.ccall('orlp_derive_public_key', 'number', ['number', 'number'], [publicKeyPtr, privateKeyPtr]);
|
||||
|
||||
if (result === 0) {
|
||||
const publicKeyBytes = this.wasm.HEAPU8.subarray(publicKeyPtr, publicKeyPtr + 32);
|
||||
return this.bytesToHex(publicKeyBytes);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private hexToBytes(hex: string): Uint8Array {
|
||||
const bytes = new Uint8Array(hex.length / 2);
|
||||
for (let i = 0; i < hex.length; i += 2) {
|
||||
bytes[i / 2] = parseInt(hex.substr(i, 2), 16);
|
||||
}
|
||||
return bytes;
|
||||
}
|
||||
|
||||
private bytesToHex(bytes: Uint8Array): string {
|
||||
return Array.from(bytes).map(b => b.toString(16).padStart(2, '0')).join('').toUpperCase();
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 3. Basic Usage
|
||||
|
||||
```typescript
|
||||
import { MeshCorePacketDecoder } from '@michaelhart/meshcore-decoder';
|
||||
import { MeshCoreWasmService } from './services/meshcore-wasm';
|
||||
|
||||
// Basic packet decoding (works immediately)
|
||||
const packet = MeshCorePacketDecoder.decode(hexData);
|
||||
|
||||
// Key derivation (wait for WASM)
|
||||
wasmService.ready.subscribe(isReady => {
|
||||
if (isReady) {
|
||||
const publicKey = wasmService.derivePublicKey(privateKeyHex);
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
### Angular/Browser: Important Notes
|
||||
|
||||
- **WASM Loading**: The library uses WebAssembly for Ed25519 key derivation. This requires proper asset configuration and a service to handle async WASM loading.
|
||||
- **Browser Compatibility**: The library automatically detects the environment and uses Web Crypto API in browsers, Node.js crypto in Node.js.
|
||||
- **Async Operations**: Key derivation is async due to WASM loading. Always wait for the `WasmService.ready` observable.
|
||||
- **Error Handling**: WASM operations may fail in some environments. Always wrap in try-catch blocks.
|
||||
|
||||
|
||||
## 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.
|
||||
3
frontend/lib/meshcore-decoder/dist/cli.d.ts
vendored
Normal file
3
frontend/lib/meshcore-decoder/dist/cli.d.ts
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
#!/usr/bin/env node
|
||||
export {};
|
||||
//# sourceMappingURL=cli.d.ts.map
|
||||
1
frontend/lib/meshcore-decoder/dist/cli.d.ts.map
vendored
Normal file
1
frontend/lib/meshcore-decoder/dist/cli.d.ts.map
vendored
Normal file
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":""}
|
||||
409
frontend/lib/meshcore-decoder/dist/cli.js
vendored
Normal file
409
frontend/lib/meshcore-decoder/dist/cli.js
vendored
Normal file
@@ -0,0 +1,409 @@
|
||||
#!/usr/bin/env node
|
||||
"use strict";
|
||||
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
||||
if (k2 === undefined) k2 = k;
|
||||
var desc = Object.getOwnPropertyDescriptor(m, k);
|
||||
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
||||
desc = { enumerable: true, get: function() { return m[k]; } };
|
||||
}
|
||||
Object.defineProperty(o, k2, desc);
|
||||
}) : (function(o, m, k, k2) {
|
||||
if (k2 === undefined) k2 = k;
|
||||
o[k2] = m[k];
|
||||
}));
|
||||
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
||||
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
||||
}) : function(o, v) {
|
||||
o["default"] = v;
|
||||
});
|
||||
var __importStar = (this && this.__importStar) || (function () {
|
||||
var ownKeys = function(o) {
|
||||
ownKeys = Object.getOwnPropertyNames || function (o) {
|
||||
var ar = [];
|
||||
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
||||
return ar;
|
||||
};
|
||||
return ownKeys(o);
|
||||
};
|
||||
return function (mod) {
|
||||
if (mod && mod.__esModule) return mod;
|
||||
var result = {};
|
||||
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
||||
__setModuleDefault(result, mod);
|
||||
return result;
|
||||
};
|
||||
})();
|
||||
var __importDefault = (this && this.__importDefault) || function (mod) {
|
||||
return (mod && mod.__esModule) ? mod : { "default": mod };
|
||||
};
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
const packet_decoder_1 = require("./decoder/packet-decoder");
|
||||
const enums_1 = require("./types/enums");
|
||||
const enum_names_1 = require("./utils/enum-names");
|
||||
const index_1 = require("./index");
|
||||
const commander_1 = require("commander");
|
||||
const chalk_1 = __importDefault(require("chalk"));
|
||||
const packageJson = __importStar(require("../package.json"));
|
||||
commander_1.program
|
||||
.name('meshcore-decoder')
|
||||
.description('CLI tool for decoding MeshCore packets')
|
||||
.version(packageJson.version);
|
||||
// Default decode command
|
||||
commander_1.program
|
||||
.command('decode', { isDefault: true })
|
||||
.description('Decode a MeshCore packet')
|
||||
.argument('<hex>', 'Hex string of the packet to decode')
|
||||
.option('-k, --key <keys...>', 'Channel secret keys for decryption (hex)')
|
||||
.option('-j, --json', 'Output as JSON instead of formatted text')
|
||||
.option('-s, --structure', 'Show detailed packet structure analysis')
|
||||
.action(async (hex, options) => {
|
||||
try {
|
||||
// Clean up hex input
|
||||
const cleanHex = hex.replace(/\s+/g, '').replace(/^0x/i, '');
|
||||
// Create key store if keys provided
|
||||
let keyStore;
|
||||
if (options.key && options.key.length > 0) {
|
||||
keyStore = packet_decoder_1.MeshCorePacketDecoder.createKeyStore({
|
||||
channelSecrets: options.key
|
||||
});
|
||||
}
|
||||
// Decode packet with signature verification
|
||||
const packet = await packet_decoder_1.MeshCorePacketDecoder.decodeWithVerification(cleanHex, { keyStore });
|
||||
if (options.json) {
|
||||
// JSON output
|
||||
if (options.structure) {
|
||||
const structure = await packet_decoder_1.MeshCorePacketDecoder.analyzeStructureWithVerification(cleanHex, { keyStore });
|
||||
console.log(JSON.stringify({ packet, structure }, null, 2));
|
||||
}
|
||||
else {
|
||||
console.log(JSON.stringify(packet, null, 2));
|
||||
}
|
||||
}
|
||||
else {
|
||||
// Formatted output
|
||||
console.log(chalk_1.default.cyan('=== MeshCore Packet Analysis ===\n'));
|
||||
if (!packet.isValid) {
|
||||
console.log(chalk_1.default.red('❌ Invalid Packet'));
|
||||
if (packet.errors) {
|
||||
packet.errors.forEach(error => console.log(chalk_1.default.red(` ${error}`)));
|
||||
}
|
||||
}
|
||||
else {
|
||||
console.log(chalk_1.default.green('✅ Valid Packet'));
|
||||
}
|
||||
console.log(`${chalk_1.default.bold('Message Hash:')} ${packet.messageHash}`);
|
||||
console.log(`${chalk_1.default.bold('Route Type:')} ${(0, enum_names_1.getRouteTypeName)(packet.routeType)}`);
|
||||
console.log(`${chalk_1.default.bold('Payload Type:')} ${(0, enum_names_1.getPayloadTypeName)(packet.payloadType)}`);
|
||||
console.log(`${chalk_1.default.bold('Total Bytes:')} ${packet.totalBytes}`);
|
||||
if (packet.path && packet.path.length > 0) {
|
||||
console.log(`${chalk_1.default.bold('Path:')} ${packet.path.join(' → ')}`);
|
||||
}
|
||||
// Show payload details (even for invalid packets)
|
||||
if (packet.payload.decoded) {
|
||||
console.log(chalk_1.default.cyan('\n=== Payload Details ==='));
|
||||
showPayloadDetails(packet.payload.decoded);
|
||||
}
|
||||
// Exit with error code if packet is invalid
|
||||
if (!packet.isValid) {
|
||||
process.exit(1);
|
||||
}
|
||||
// Show structure if requested
|
||||
if (options.structure) {
|
||||
const structure = await packet_decoder_1.MeshCorePacketDecoder.analyzeStructureWithVerification(cleanHex, { keyStore });
|
||||
console.log(chalk_1.default.cyan('\n=== Packet Structure ==='));
|
||||
console.log(chalk_1.default.yellow('\nMain Segments:'));
|
||||
structure.segments.forEach((seg, i) => {
|
||||
console.log(`${i + 1}. ${chalk_1.default.bold(seg.name)} (bytes ${seg.startByte}-${seg.endByte}): ${seg.value}`);
|
||||
if (seg.description) {
|
||||
console.log(` ${chalk_1.default.dim(seg.description)}`);
|
||||
}
|
||||
});
|
||||
if (structure.payload.segments.length > 0) {
|
||||
console.log(chalk_1.default.yellow('\nPayload Segments:'));
|
||||
structure.payload.segments.forEach((seg, i) => {
|
||||
console.log(`${i + 1}. ${chalk_1.default.bold(seg.name)} (bytes ${seg.startByte}-${seg.endByte}): ${seg.value}`);
|
||||
console.log(` ${chalk_1.default.dim(seg.description)}`);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (error) {
|
||||
console.error(chalk_1.default.red('Error:'), error.message);
|
||||
process.exit(1);
|
||||
}
|
||||
});
|
||||
function showPayloadDetails(payload) {
|
||||
switch (payload.type) {
|
||||
case enums_1.PayloadType.Advert:
|
||||
const advert = payload;
|
||||
console.log(`${chalk_1.default.bold('Device Role:')} ${(0, enum_names_1.getDeviceRoleName)(advert.appData.deviceRole)}`);
|
||||
if (advert.appData.name) {
|
||||
console.log(`${chalk_1.default.bold('Device Name:')} ${advert.appData.name}`);
|
||||
}
|
||||
if (advert.appData.location) {
|
||||
console.log(`${chalk_1.default.bold('Location:')} ${advert.appData.location.latitude}, ${advert.appData.location.longitude}`);
|
||||
}
|
||||
console.log(`${chalk_1.default.bold('Timestamp:')} ${new Date(advert.timestamp * 1000).toISOString()}`);
|
||||
// Show signature verification status
|
||||
if (advert.signatureValid !== undefined) {
|
||||
if (advert.signatureValid) {
|
||||
console.log(`${chalk_1.default.bold('Signature:')} ${chalk_1.default.green('✅ Valid Ed25519 signature')}`);
|
||||
}
|
||||
else {
|
||||
console.log(`${chalk_1.default.bold('Signature:')} ${chalk_1.default.red('❌ Invalid Ed25519 signature')}`);
|
||||
if (advert.signatureError) {
|
||||
console.log(`${chalk_1.default.bold('Error:')} ${chalk_1.default.red(advert.signatureError)}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
console.log(`${chalk_1.default.bold('Signature:')} ${chalk_1.default.yellow('⚠️ Not verified (use async verification)')}`);
|
||||
}
|
||||
break;
|
||||
case enums_1.PayloadType.GroupText:
|
||||
const groupText = payload;
|
||||
console.log(`${chalk_1.default.bold('Channel Hash:')} ${groupText.channelHash}`);
|
||||
if (groupText.decrypted) {
|
||||
console.log(chalk_1.default.green('🔓 Decrypted Message:'));
|
||||
if (groupText.decrypted.sender) {
|
||||
console.log(`${chalk_1.default.bold('Sender:')} ${groupText.decrypted.sender}`);
|
||||
}
|
||||
console.log(`${chalk_1.default.bold('Message:')} ${groupText.decrypted.message}`);
|
||||
console.log(`${chalk_1.default.bold('Timestamp:')} ${new Date(groupText.decrypted.timestamp * 1000).toISOString()}`);
|
||||
}
|
||||
else {
|
||||
console.log(chalk_1.default.yellow('🔒 Encrypted (no key available)'));
|
||||
console.log(`${chalk_1.default.bold('Ciphertext:')} ${groupText.ciphertext.substring(0, 32)}...`);
|
||||
}
|
||||
break;
|
||||
case enums_1.PayloadType.Trace:
|
||||
const trace = payload;
|
||||
console.log(`${chalk_1.default.bold('Trace Tag:')} ${trace.traceTag}`);
|
||||
console.log(`${chalk_1.default.bold('Auth Code:')} ${trace.authCode}`);
|
||||
if (trace.snrValues && trace.snrValues.length > 0) {
|
||||
console.log(`${chalk_1.default.bold('SNR Values:')} ${trace.snrValues.map(snr => `${snr.toFixed(1)}dB`).join(', ')}`);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
console.log(`${chalk_1.default.bold('Type:')} ${(0, enum_names_1.getPayloadTypeName)(payload.type)}`);
|
||||
console.log(`${chalk_1.default.bold('Valid:')} ${payload.isValid ? '✅' : '❌'}`);
|
||||
}
|
||||
}
|
||||
// Add key derivation command
|
||||
commander_1.program
|
||||
.command('derive-key')
|
||||
.description('Derive Ed25519 public key from MeshCore private key')
|
||||
.argument('<private-key>', '64-byte private key in hex format')
|
||||
.option('-v, --validate <public-key>', 'Validate against expected public key')
|
||||
.option('-j, --json', 'Output as JSON')
|
||||
.action(async (privateKeyHex, options) => {
|
||||
try {
|
||||
// Clean up hex input
|
||||
const cleanPrivateKey = privateKeyHex.replace(/\s+/g, '').replace(/^0x/i, '');
|
||||
if (cleanPrivateKey.length !== 128) {
|
||||
console.error(chalk_1.default.red('❌ Error: Private key must be exactly 64 bytes (128 hex characters)'));
|
||||
process.exit(1);
|
||||
}
|
||||
if (options.json) {
|
||||
// JSON output
|
||||
const result = {
|
||||
privateKey: cleanPrivateKey,
|
||||
derivedPublicKey: await index_1.Utils.derivePublicKey(cleanPrivateKey)
|
||||
};
|
||||
if (options.validate) {
|
||||
const cleanExpectedKey = options.validate.replace(/\s+/g, '').replace(/^0x/i, '');
|
||||
result.expectedPublicKey = cleanExpectedKey;
|
||||
result.isValid = await index_1.Utils.validateKeyPair(cleanPrivateKey, cleanExpectedKey);
|
||||
result.match = result.derivedPublicKey.toLowerCase() === cleanExpectedKey.toLowerCase();
|
||||
}
|
||||
console.log(JSON.stringify(result, null, 2));
|
||||
}
|
||||
else {
|
||||
// Formatted output
|
||||
console.log(chalk_1.default.cyan('=== MeshCore Ed25519 Key Derivation ===\n'));
|
||||
console.log(chalk_1.default.bold('Private Key (64 bytes):'));
|
||||
console.log(chalk_1.default.gray(cleanPrivateKey));
|
||||
console.log();
|
||||
console.log(chalk_1.default.bold('Derived Public Key (32 bytes):'));
|
||||
const derivedKey = await index_1.Utils.derivePublicKey(cleanPrivateKey);
|
||||
console.log(chalk_1.default.green(derivedKey));
|
||||
console.log();
|
||||
if (options.validate) {
|
||||
const cleanExpectedKey = options.validate.replace(/\s+/g, '').replace(/^0x/i, '');
|
||||
console.log(chalk_1.default.bold('Expected Public Key:'));
|
||||
console.log(chalk_1.default.gray(cleanExpectedKey));
|
||||
console.log();
|
||||
const match = derivedKey.toLowerCase() === cleanExpectedKey.toLowerCase();
|
||||
console.log(chalk_1.default.bold('Validation:'));
|
||||
console.log(match ? chalk_1.default.green('Keys match') : chalk_1.default.red('Keys do not match'));
|
||||
if (!match) {
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
console.log(chalk_1.default.green('Key derivation completed successfully'));
|
||||
}
|
||||
}
|
||||
catch (error) {
|
||||
const errorMessage = error instanceof Error ? error.message : 'Unknown error';
|
||||
if (options.json) {
|
||||
console.log(JSON.stringify({ error: errorMessage }, null, 2));
|
||||
}
|
||||
else {
|
||||
console.error(chalk_1.default.red(`Error: ${errorMessage}`));
|
||||
}
|
||||
process.exit(1);
|
||||
}
|
||||
});
|
||||
// Add auth-token command
|
||||
commander_1.program
|
||||
.command('auth-token')
|
||||
.description('Generate JWT authentication token signed with Ed25519 private key')
|
||||
.argument('<public-key>', '32-byte public key in hex format')
|
||||
.argument('<private-key>', '64-byte private key in hex format')
|
||||
.option('-e, --exp <seconds>', 'Token expiration in seconds from now (default: 86400 = 24 hours)', '86400')
|
||||
.option('-c, --claims <json>', 'Additional claims as JSON object (e.g., \'{"aud":"mqtt.example.com","sub":"device-123"}\')')
|
||||
.option('-j, --json', 'Output as JSON')
|
||||
.action(async (publicKeyHex, privateKeyHex, options) => {
|
||||
try {
|
||||
const { createAuthToken } = await Promise.resolve().then(() => __importStar(require('./utils/auth-token')));
|
||||
// Clean up hex inputs
|
||||
const cleanPublicKey = publicKeyHex.replace(/\s+/g, '').replace(/^0x/i, '');
|
||||
const cleanPrivateKey = privateKeyHex.replace(/\s+/g, '').replace(/^0x/i, '');
|
||||
if (cleanPublicKey.length !== 64) {
|
||||
console.error(chalk_1.default.red('❌ Error: Public key must be exactly 32 bytes (64 hex characters)'));
|
||||
process.exit(1);
|
||||
}
|
||||
if (cleanPrivateKey.length !== 128) {
|
||||
console.error(chalk_1.default.red('❌ Error: Private key must be exactly 64 bytes (128 hex characters)'));
|
||||
process.exit(1);
|
||||
}
|
||||
const expSeconds = parseInt(options.exp);
|
||||
const iat = Math.floor(Date.now() / 1000);
|
||||
const exp = iat + expSeconds;
|
||||
const payload = {
|
||||
publicKey: cleanPublicKey.toUpperCase(),
|
||||
iat,
|
||||
exp
|
||||
};
|
||||
// Parse and merge additional claims if provided
|
||||
if (options.claims) {
|
||||
try {
|
||||
const additionalClaims = JSON.parse(options.claims);
|
||||
Object.assign(payload, additionalClaims);
|
||||
}
|
||||
catch (e) {
|
||||
console.error(chalk_1.default.red('❌ Error: Invalid JSON in --claims option'));
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
const token = await createAuthToken(payload, cleanPrivateKey, cleanPublicKey.toUpperCase());
|
||||
if (options.json) {
|
||||
console.log(JSON.stringify({
|
||||
token,
|
||||
payload
|
||||
}, null, 2));
|
||||
}
|
||||
else {
|
||||
console.log(token);
|
||||
}
|
||||
}
|
||||
catch (error) {
|
||||
const errorMessage = error instanceof Error ? error.message : 'Unknown error';
|
||||
if (options.json) {
|
||||
console.log(JSON.stringify({ error: errorMessage }, null, 2));
|
||||
}
|
||||
else {
|
||||
console.error(chalk_1.default.red(`Error: ${errorMessage}`));
|
||||
}
|
||||
process.exit(1);
|
||||
}
|
||||
});
|
||||
// Add verify-token command
|
||||
commander_1.program
|
||||
.command('verify-token')
|
||||
.description('Verify JWT authentication token')
|
||||
.argument('<token>', 'JWT token to verify')
|
||||
.option('-p, --public-key <key>', 'Expected public key in hex format (optional)')
|
||||
.option('-j, --json', 'Output as JSON')
|
||||
.action(async (token, options) => {
|
||||
try {
|
||||
const { verifyAuthToken } = await Promise.resolve().then(() => __importStar(require('./utils/auth-token')));
|
||||
const cleanToken = token.trim();
|
||||
let expectedPublicKey;
|
||||
if (options.publicKey) {
|
||||
const cleanKey = options.publicKey.replace(/\s+/g, '').replace(/^0x/i, '').toUpperCase();
|
||||
if (cleanKey.length !== 64) {
|
||||
console.error(chalk_1.default.red('❌ Error: Public key must be exactly 32 bytes (64 hex characters)'));
|
||||
process.exit(1);
|
||||
}
|
||||
expectedPublicKey = cleanKey;
|
||||
}
|
||||
const payload = await verifyAuthToken(cleanToken, expectedPublicKey);
|
||||
if (payload) {
|
||||
const now = Math.floor(Date.now() / 1000);
|
||||
const isExpired = payload.exp && now > payload.exp;
|
||||
const timeToExpiry = payload.exp ? payload.exp - now : null;
|
||||
if (options.json) {
|
||||
console.log(JSON.stringify({
|
||||
valid: true,
|
||||
expired: isExpired,
|
||||
payload,
|
||||
timeToExpiry
|
||||
}, null, 2));
|
||||
}
|
||||
else {
|
||||
console.log(chalk_1.default.green('✅ Token is valid'));
|
||||
console.log(chalk_1.default.cyan('\nPayload:'));
|
||||
console.log(` Public Key: ${payload.publicKey}`);
|
||||
console.log(` Issued At: ${new Date(payload.iat * 1000).toISOString()} (${payload.iat})`);
|
||||
if (payload.exp) {
|
||||
console.log(` Expires At: ${new Date(payload.exp * 1000).toISOString()} (${payload.exp})`);
|
||||
if (isExpired) {
|
||||
console.log(chalk_1.default.red(` Status: EXPIRED`));
|
||||
}
|
||||
else {
|
||||
console.log(chalk_1.default.green(` Status: Valid for ${timeToExpiry} more seconds`));
|
||||
}
|
||||
}
|
||||
// Show any additional claims
|
||||
const standardClaims = ['publicKey', 'iat', 'exp'];
|
||||
const customClaims = Object.keys(payload).filter(k => !standardClaims.includes(k));
|
||||
if (customClaims.length > 0) {
|
||||
console.log(chalk_1.default.cyan('\nCustom Claims:'));
|
||||
customClaims.forEach(key => {
|
||||
console.log(` ${key}: ${JSON.stringify(payload[key])}`);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (options.json) {
|
||||
console.log(JSON.stringify({
|
||||
valid: false,
|
||||
error: 'Token verification failed'
|
||||
}, null, 2));
|
||||
}
|
||||
else {
|
||||
console.error(chalk_1.default.red('❌ Token verification failed'));
|
||||
console.error(chalk_1.default.yellow('Possible reasons:'));
|
||||
console.error(' - Invalid signature');
|
||||
console.error(' - Token format is incorrect');
|
||||
console.error(' - Public key mismatch (if --public-key was provided)');
|
||||
}
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
catch (error) {
|
||||
const errorMessage = error instanceof Error ? error.message : 'Unknown error';
|
||||
if (options.json) {
|
||||
console.log(JSON.stringify({ valid: false, error: errorMessage }, null, 2));
|
||||
}
|
||||
else {
|
||||
console.error(chalk_1.default.red(`Error: ${errorMessage}`));
|
||||
}
|
||||
process.exit(1);
|
||||
}
|
||||
});
|
||||
commander_1.program.parse();
|
||||
//# sourceMappingURL=cli.js.map
|
||||
1
frontend/lib/meshcore-decoder/dist/cli.js.map
vendored
Normal file
1
frontend/lib/meshcore-decoder/dist/cli.js.map
vendored
Normal file
File diff suppressed because one or more lines are too long
15
frontend/lib/meshcore-decoder/dist/crypto/channel-crypto.d.ts
vendored
Normal file
15
frontend/lib/meshcore-decoder/dist/crypto/channel-crypto.d.ts
vendored
Normal file
@@ -0,0 +1,15 @@
|
||||
import { DecryptionResult } from '../types/crypto';
|
||||
export declare class ChannelCrypto {
|
||||
/**
|
||||
* Decrypt GroupText message using MeshCore algorithm:
|
||||
* - HMAC-SHA256 verification with 2-byte MAC
|
||||
* - AES-128 ECB decryption
|
||||
*/
|
||||
static decryptGroupTextMessage(ciphertext: string, cipherMac: string, channelKey: string): DecryptionResult;
|
||||
/**
|
||||
* Calculate MeshCore channel hash from secret key
|
||||
* Returns the first byte of SHA256(secret) as hex string
|
||||
*/
|
||||
static calculateChannelHash(secretKeyHex: string): string;
|
||||
}
|
||||
//# sourceMappingURL=channel-crypto.d.ts.map
|
||||
1
frontend/lib/meshcore-decoder/dist/crypto/channel-crypto.d.ts.map
vendored
Normal file
1
frontend/lib/meshcore-decoder/dist/crypto/channel-crypto.d.ts.map
vendored
Normal file
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"channel-crypto.d.ts","sourceRoot":"","sources":["../../src/crypto/channel-crypto.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,gBAAgB,EAAE,MAAM,iBAAiB,CAAC;AAGnD,qBAAa,aAAa;IACxB;;;;OAIG;IACH,MAAM,CAAC,uBAAuB,CAC5B,UAAU,EAAE,MAAM,EAClB,SAAS,EAAE,MAAM,EACjB,UAAU,EAAE,MAAM,GACjB,gBAAgB;IAuFnB;;;OAGG;IACH,MAAM,CAAC,oBAAoB,CAAC,YAAY,EAAE,MAAM,GAAG,MAAM;CAK1D"}
|
||||
94
frontend/lib/meshcore-decoder/dist/crypto/channel-crypto.js
vendored
Normal file
94
frontend/lib/meshcore-decoder/dist/crypto/channel-crypto.js
vendored
Normal file
@@ -0,0 +1,94 @@
|
||||
"use strict";
|
||||
// Copyright (c) 2025 Michael Hart: https://github.com/michaelhart/meshcore-decoder
|
||||
// MIT License
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.ChannelCrypto = void 0;
|
||||
const crypto_js_1 = require("crypto-js");
|
||||
const hex_1 = require("../utils/hex");
|
||||
class ChannelCrypto {
|
||||
/**
|
||||
* Decrypt GroupText message using MeshCore algorithm:
|
||||
* - HMAC-SHA256 verification with 2-byte MAC
|
||||
* - AES-128 ECB decryption
|
||||
*/
|
||||
static decryptGroupTextMessage(ciphertext, cipherMac, channelKey) {
|
||||
try {
|
||||
// convert hex strings to byte arrays
|
||||
const channelKey16 = (0, hex_1.hexToBytes)(channelKey);
|
||||
const macBytes = (0, hex_1.hexToBytes)(cipherMac);
|
||||
// MeshCore uses 32-byte channel secret: 16-byte key + 16 zero bytes
|
||||
const channelSecret = new Uint8Array(32);
|
||||
channelSecret.set(channelKey16, 0);
|
||||
// Step 1: Verify HMAC-SHA256 using full 32-byte channel secret
|
||||
const calculatedMac = (0, crypto_js_1.HmacSHA256)(crypto_js_1.enc.Hex.parse(ciphertext), crypto_js_1.enc.Hex.parse((0, hex_1.bytesToHex)(channelSecret)));
|
||||
const calculatedMacBytes = (0, hex_1.hexToBytes)(calculatedMac.toString(crypto_js_1.enc.Hex));
|
||||
const calculatedMacFirst2 = calculatedMacBytes.slice(0, 2);
|
||||
if (calculatedMacFirst2[0] !== macBytes[0] || calculatedMacFirst2[1] !== macBytes[1]) {
|
||||
return { success: false, error: 'MAC verification failed' };
|
||||
}
|
||||
// Step 2: Decrypt using AES-128 ECB with first 16 bytes of channel secret
|
||||
const keyWords = crypto_js_1.enc.Hex.parse(channelKey);
|
||||
const ciphertextWords = crypto_js_1.enc.Hex.parse(ciphertext);
|
||||
const decrypted = crypto_js_1.AES.decrypt(crypto_js_1.lib.CipherParams.create({ ciphertext: ciphertextWords }), keyWords, { mode: crypto_js_1.mode.ECB, padding: crypto_js_1.pad.NoPadding });
|
||||
const decryptedBytes = (0, hex_1.hexToBytes)(decrypted.toString(crypto_js_1.enc.Hex));
|
||||
if (!decryptedBytes || decryptedBytes.length < 5) {
|
||||
return { success: false, error: 'Decrypted content too short' };
|
||||
}
|
||||
// parse MeshCore format: timestamp(4) + flags(1) + message_text
|
||||
const timestamp = decryptedBytes[0] |
|
||||
(decryptedBytes[1] << 8) |
|
||||
(decryptedBytes[2] << 16) |
|
||||
(decryptedBytes[3] << 24);
|
||||
const flagsAndAttempt = decryptedBytes[4];
|
||||
// extract message text with UTF-8 decoding
|
||||
const messageBytes = decryptedBytes.slice(5);
|
||||
const decoder = new TextDecoder('utf-8');
|
||||
let messageText = decoder.decode(messageBytes);
|
||||
// remove null terminator if present
|
||||
const nullIndex = messageText.indexOf('\0');
|
||||
if (nullIndex >= 0) {
|
||||
messageText = messageText.substring(0, nullIndex);
|
||||
}
|
||||
// parse sender and message (format: "sender: message")
|
||||
const colonIndex = messageText.indexOf(': ');
|
||||
let sender;
|
||||
let content;
|
||||
if (colonIndex > 0 && colonIndex < 50) {
|
||||
const potentialSender = messageText.substring(0, colonIndex);
|
||||
if (!/[:\[\]]/.test(potentialSender)) {
|
||||
sender = potentialSender;
|
||||
content = messageText.substring(colonIndex + 2);
|
||||
}
|
||||
else {
|
||||
content = messageText;
|
||||
}
|
||||
}
|
||||
else {
|
||||
content = messageText;
|
||||
}
|
||||
return {
|
||||
success: true,
|
||||
data: {
|
||||
timestamp,
|
||||
flags: flagsAndAttempt,
|
||||
sender,
|
||||
message: content
|
||||
}
|
||||
};
|
||||
}
|
||||
catch (error) {
|
||||
return { success: false, error: error instanceof Error ? error.message : 'Decryption failed' };
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Calculate MeshCore channel hash from secret key
|
||||
* Returns the first byte of SHA256(secret) as hex string
|
||||
*/
|
||||
static calculateChannelHash(secretKeyHex) {
|
||||
const hash = (0, crypto_js_1.SHA256)(crypto_js_1.enc.Hex.parse(secretKeyHex));
|
||||
const hashBytes = (0, hex_1.hexToBytes)(hash.toString(crypto_js_1.enc.Hex));
|
||||
return hashBytes[0].toString(16).padStart(2, '0');
|
||||
}
|
||||
}
|
||||
exports.ChannelCrypto = ChannelCrypto;
|
||||
//# sourceMappingURL=channel-crypto.js.map
|
||||
1
frontend/lib/meshcore-decoder/dist/crypto/channel-crypto.js.map
vendored
Normal file
1
frontend/lib/meshcore-decoder/dist/crypto/channel-crypto.js.map
vendored
Normal file
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"channel-crypto.js","sourceRoot":"","sources":["../../src/crypto/channel-crypto.ts"],"names":[],"mappings":"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"}
|
||||
48
frontend/lib/meshcore-decoder/dist/crypto/ed25519-verifier.d.ts
vendored
Normal file
48
frontend/lib/meshcore-decoder/dist/crypto/ed25519-verifier.d.ts
vendored
Normal file
@@ -0,0 +1,48 @@
|
||||
export declare class Ed25519SignatureVerifier {
|
||||
/**
|
||||
* Verify an Ed25519 signature for MeshCore advertisement packets
|
||||
*
|
||||
* According to MeshCore protocol, the signed message for advertisements is:
|
||||
* timestamp (4 bytes LE) + flags (1 byte) + location (8 bytes LE, if present) + name (variable, if present)
|
||||
*/
|
||||
static verifyAdvertisementSignature(publicKeyHex: string, signatureHex: string, timestamp: number, appDataHex: string): Promise<boolean>;
|
||||
/**
|
||||
* Construct the signed message for MeshCore advertisements
|
||||
* According to MeshCore source (Mesh.cpp lines 242-248):
|
||||
* Format: public_key (32 bytes) + timestamp (4 bytes LE) + app_data (variable length)
|
||||
*/
|
||||
private static constructAdvertSignedMessage;
|
||||
/**
|
||||
* Get a human-readable description of what was signed
|
||||
*/
|
||||
static getSignedMessageDescription(publicKeyHex: string, timestamp: number, appDataHex: string): string;
|
||||
/**
|
||||
* Get the hex representation of the signed message for debugging
|
||||
*/
|
||||
static getSignedMessageHex(publicKeyHex: string, timestamp: number, appDataHex: string): string;
|
||||
/**
|
||||
* Derive Ed25519 public key from orlp/ed25519 private key format
|
||||
* This implements the same algorithm as orlp/ed25519's ed25519_derive_pub()
|
||||
*
|
||||
* @param privateKeyHex - 64-byte private key in hex format (orlp/ed25519 format)
|
||||
* @returns 32-byte public key in hex format
|
||||
*/
|
||||
static derivePublicKey(privateKeyHex: string): Promise<string>;
|
||||
/**
|
||||
* Derive Ed25519 public key from orlp/ed25519 private key format (synchronous version)
|
||||
* This implements the same algorithm as orlp/ed25519's ed25519_derive_pub()
|
||||
*
|
||||
* @param privateKeyHex - 64-byte private key in hex format (orlp/ed25519 format)
|
||||
* @returns 32-byte public key in hex format
|
||||
*/
|
||||
static derivePublicKeySync(privateKeyHex: string): string;
|
||||
/**
|
||||
* Validate that a private key correctly derives to the expected public key
|
||||
*
|
||||
* @param privateKeyHex - 64-byte private key in hex format
|
||||
* @param expectedPublicKeyHex - Expected 32-byte public key in hex format
|
||||
* @returns true if the private key derives to the expected public key
|
||||
*/
|
||||
static validateKeyPair(privateKeyHex: string, expectedPublicKeyHex: string): Promise<boolean>;
|
||||
}
|
||||
//# sourceMappingURL=ed25519-verifier.d.ts.map
|
||||
1
frontend/lib/meshcore-decoder/dist/crypto/ed25519-verifier.d.ts.map
vendored
Normal file
1
frontend/lib/meshcore-decoder/dist/crypto/ed25519-verifier.d.ts.map
vendored
Normal file
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"ed25519-verifier.d.ts","sourceRoot":"","sources":["../../src/crypto/ed25519-verifier.ts"],"names":[],"mappings":"AAyEA,qBAAa,wBAAwB;IACnC;;;;;OAKG;WACU,4BAA4B,CACvC,YAAY,EAAE,MAAM,EACpB,YAAY,EAAE,MAAM,EACpB,SAAS,EAAE,MAAM,EACjB,UAAU,EAAE,MAAM,GACjB,OAAO,CAAC,OAAO,CAAC;IAkBnB;;;;OAIG;IACH,OAAO,CAAC,MAAM,CAAC,4BAA4B;IAuB3C;;OAEG;IACH,MAAM,CAAC,2BAA2B,CAChC,YAAY,EAAE,MAAM,EACpB,SAAS,EAAE,MAAM,EACjB,UAAU,EAAE,MAAM,GACjB,MAAM;IAIT;;OAEG;IACH,MAAM,CAAC,mBAAmB,CACxB,YAAY,EAAE,MAAM,EACpB,SAAS,EAAE,MAAM,EACjB,UAAU,EAAE,MAAM,GACjB,MAAM;IAMT;;;;;;OAMG;WACU,eAAe,CAAC,aAAa,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAepE;;;;;;OAMG;IACH,MAAM,CAAC,mBAAmB,CAAC,aAAa,EAAE,MAAM,GAAG,MAAM;IAezD;;;;;;OAMG;WACU,eAAe,CAAC,aAAa,EAAE,MAAM,EAAE,oBAAoB,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;CAQpG"}
|
||||
217
frontend/lib/meshcore-decoder/dist/crypto/ed25519-verifier.js
vendored
Normal file
217
frontend/lib/meshcore-decoder/dist/crypto/ed25519-verifier.js
vendored
Normal file
@@ -0,0 +1,217 @@
|
||||
"use strict";
|
||||
// Copyright (c) 2025 Michael Hart: https://github.com/michaelhart/meshcore-decoder
|
||||
// MIT License
|
||||
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
||||
if (k2 === undefined) k2 = k;
|
||||
var desc = Object.getOwnPropertyDescriptor(m, k);
|
||||
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
||||
desc = { enumerable: true, get: function() { return m[k]; } };
|
||||
}
|
||||
Object.defineProperty(o, k2, desc);
|
||||
}) : (function(o, m, k, k2) {
|
||||
if (k2 === undefined) k2 = k;
|
||||
o[k2] = m[k];
|
||||
}));
|
||||
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
||||
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
||||
}) : function(o, v) {
|
||||
o["default"] = v;
|
||||
});
|
||||
var __importStar = (this && this.__importStar) || (function () {
|
||||
var ownKeys = function(o) {
|
||||
ownKeys = Object.getOwnPropertyNames || function (o) {
|
||||
var ar = [];
|
||||
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
||||
return ar;
|
||||
};
|
||||
return ownKeys(o);
|
||||
};
|
||||
return function (mod) {
|
||||
if (mod && mod.__esModule) return mod;
|
||||
var result = {};
|
||||
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
||||
__setModuleDefault(result, mod);
|
||||
return result;
|
||||
};
|
||||
})();
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.Ed25519SignatureVerifier = void 0;
|
||||
const ed25519 = __importStar(require("@noble/ed25519"));
|
||||
const hex_1 = require("../utils/hex");
|
||||
const orlp_ed25519_wasm_1 = require("./orlp-ed25519-wasm");
|
||||
// Cross-platform SHA-512 implementation
|
||||
async function sha512Hash(data) {
|
||||
// Browser environment - use Web Crypto API
|
||||
if (typeof globalThis !== 'undefined' && globalThis.crypto && globalThis.crypto.subtle) {
|
||||
const hashBuffer = await globalThis.crypto.subtle.digest('SHA-512', data);
|
||||
return new Uint8Array(hashBuffer);
|
||||
}
|
||||
// Node.js environment - use crypto module
|
||||
if (typeof require !== 'undefined') {
|
||||
try {
|
||||
const { createHash } = require('crypto');
|
||||
return createHash('sha512').update(data).digest();
|
||||
}
|
||||
catch (error) {
|
||||
// Fallback for environments where require is not available
|
||||
}
|
||||
}
|
||||
throw new Error('No SHA-512 implementation available');
|
||||
}
|
||||
function sha512HashSync(data) {
|
||||
// Node.js environment - use crypto module
|
||||
if (typeof require !== 'undefined') {
|
||||
try {
|
||||
const { createHash } = require('crypto');
|
||||
return createHash('sha512').update(data).digest();
|
||||
}
|
||||
catch (error) {
|
||||
// Fallback
|
||||
}
|
||||
}
|
||||
// Browser environment fallback - use crypto-js for sync operation
|
||||
try {
|
||||
const CryptoJS = require('crypto-js');
|
||||
const wordArray = CryptoJS.lib.WordArray.create(data);
|
||||
const hash = CryptoJS.SHA512(wordArray);
|
||||
const hashBytes = new Uint8Array(64);
|
||||
// Convert CryptoJS hash to Uint8Array
|
||||
for (let i = 0; i < 16; i++) {
|
||||
const word = hash.words[i] || 0;
|
||||
hashBytes[i * 4] = (word >>> 24) & 0xff;
|
||||
hashBytes[i * 4 + 1] = (word >>> 16) & 0xff;
|
||||
hashBytes[i * 4 + 2] = (word >>> 8) & 0xff;
|
||||
hashBytes[i * 4 + 3] = word & 0xff;
|
||||
}
|
||||
return hashBytes;
|
||||
}
|
||||
catch (error) {
|
||||
// Final fallback - this should not happen since crypto-js is a dependency
|
||||
throw new Error('No SHA-512 implementation available for synchronous operation');
|
||||
}
|
||||
}
|
||||
// Set up SHA-512 for @noble/ed25519
|
||||
ed25519.etc.sha512Async = sha512Hash;
|
||||
// Always set up sync version - @noble/ed25519 requires it
|
||||
// It will throw in browser environments, which @noble/ed25519 can handle
|
||||
try {
|
||||
ed25519.etc.sha512Sync = sha512HashSync;
|
||||
}
|
||||
catch (error) {
|
||||
console.debug('Could not set up synchronous SHA-512:', error);
|
||||
}
|
||||
class Ed25519SignatureVerifier {
|
||||
/**
|
||||
* Verify an Ed25519 signature for MeshCore advertisement packets
|
||||
*
|
||||
* According to MeshCore protocol, the signed message for advertisements is:
|
||||
* timestamp (4 bytes LE) + flags (1 byte) + location (8 bytes LE, if present) + name (variable, if present)
|
||||
*/
|
||||
static async verifyAdvertisementSignature(publicKeyHex, signatureHex, timestamp, appDataHex) {
|
||||
try {
|
||||
// Convert hex strings to Uint8Arrays
|
||||
const publicKey = (0, hex_1.hexToBytes)(publicKeyHex);
|
||||
const signature = (0, hex_1.hexToBytes)(signatureHex);
|
||||
const appData = (0, hex_1.hexToBytes)(appDataHex);
|
||||
// Construct the signed message according to MeshCore format
|
||||
const message = this.constructAdvertSignedMessage(publicKeyHex, timestamp, appData);
|
||||
// Verify the signature using noble-ed25519
|
||||
return await ed25519.verify(signature, message, publicKey);
|
||||
}
|
||||
catch (error) {
|
||||
console.error('Ed25519 signature verification failed:', error);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Construct the signed message for MeshCore advertisements
|
||||
* According to MeshCore source (Mesh.cpp lines 242-248):
|
||||
* Format: public_key (32 bytes) + timestamp (4 bytes LE) + app_data (variable length)
|
||||
*/
|
||||
static constructAdvertSignedMessage(publicKeyHex, timestamp, appData) {
|
||||
const publicKey = (0, hex_1.hexToBytes)(publicKeyHex);
|
||||
// Timestamp (4 bytes, little-endian)
|
||||
const timestampBytes = new Uint8Array(4);
|
||||
timestampBytes[0] = timestamp & 0xFF;
|
||||
timestampBytes[1] = (timestamp >> 8) & 0xFF;
|
||||
timestampBytes[2] = (timestamp >> 16) & 0xFF;
|
||||
timestampBytes[3] = (timestamp >> 24) & 0xFF;
|
||||
// Concatenate: public_key + timestamp + app_data
|
||||
const message = new Uint8Array(32 + 4 + appData.length);
|
||||
message.set(publicKey, 0);
|
||||
message.set(timestampBytes, 32);
|
||||
message.set(appData, 36);
|
||||
return message;
|
||||
}
|
||||
/**
|
||||
* Get a human-readable description of what was signed
|
||||
*/
|
||||
static getSignedMessageDescription(publicKeyHex, timestamp, appDataHex) {
|
||||
return `Public Key: ${publicKeyHex} + Timestamp: ${timestamp} (${new Date(timestamp * 1000).toISOString()}) + App Data: ${appDataHex}`;
|
||||
}
|
||||
/**
|
||||
* Get the hex representation of the signed message for debugging
|
||||
*/
|
||||
static getSignedMessageHex(publicKeyHex, timestamp, appDataHex) {
|
||||
const appData = (0, hex_1.hexToBytes)(appDataHex);
|
||||
const message = this.constructAdvertSignedMessage(publicKeyHex, timestamp, appData);
|
||||
return (0, hex_1.bytesToHex)(message);
|
||||
}
|
||||
/**
|
||||
* Derive Ed25519 public key from orlp/ed25519 private key format
|
||||
* This implements the same algorithm as orlp/ed25519's ed25519_derive_pub()
|
||||
*
|
||||
* @param privateKeyHex - 64-byte private key in hex format (orlp/ed25519 format)
|
||||
* @returns 32-byte public key in hex format
|
||||
*/
|
||||
static async derivePublicKey(privateKeyHex) {
|
||||
try {
|
||||
const privateKeyBytes = (0, hex_1.hexToBytes)(privateKeyHex);
|
||||
if (privateKeyBytes.length !== 64) {
|
||||
throw new Error(`Invalid private key length: expected 64 bytes, got ${privateKeyBytes.length}`);
|
||||
}
|
||||
// Use the orlp/ed25519 WebAssembly implementation
|
||||
return await (0, orlp_ed25519_wasm_1.derivePublicKey)(privateKeyHex);
|
||||
}
|
||||
catch (error) {
|
||||
throw new Error(`Failed to derive public key: ${error instanceof Error ? error.message : 'Unknown error'}`);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Derive Ed25519 public key from orlp/ed25519 private key format (synchronous version)
|
||||
* This implements the same algorithm as orlp/ed25519's ed25519_derive_pub()
|
||||
*
|
||||
* @param privateKeyHex - 64-byte private key in hex format (orlp/ed25519 format)
|
||||
* @returns 32-byte public key in hex format
|
||||
*/
|
||||
static derivePublicKeySync(privateKeyHex) {
|
||||
try {
|
||||
const privateKeyBytes = (0, hex_1.hexToBytes)(privateKeyHex);
|
||||
if (privateKeyBytes.length !== 64) {
|
||||
throw new Error(`Invalid private key length: expected 64 bytes, got ${privateKeyBytes.length}`);
|
||||
}
|
||||
// Note: WASM operations are async, so this sync version throws an error
|
||||
throw new Error('Synchronous key derivation not supported with WASM. Use derivePublicKey() instead.');
|
||||
}
|
||||
catch (error) {
|
||||
throw new Error(`Failed to derive public key: ${error instanceof Error ? error.message : 'Unknown error'}`);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Validate that a private key correctly derives to the expected public key
|
||||
*
|
||||
* @param privateKeyHex - 64-byte private key in hex format
|
||||
* @param expectedPublicKeyHex - Expected 32-byte public key in hex format
|
||||
* @returns true if the private key derives to the expected public key
|
||||
*/
|
||||
static async validateKeyPair(privateKeyHex, expectedPublicKeyHex) {
|
||||
try {
|
||||
return await (0, orlp_ed25519_wasm_1.validateKeyPair)(privateKeyHex, expectedPublicKeyHex);
|
||||
}
|
||||
catch (error) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
exports.Ed25519SignatureVerifier = Ed25519SignatureVerifier;
|
||||
//# sourceMappingURL=ed25519-verifier.js.map
|
||||
1
frontend/lib/meshcore-decoder/dist/crypto/ed25519-verifier.js.map
vendored
Normal file
1
frontend/lib/meshcore-decoder/dist/crypto/ed25519-verifier.js.map
vendored
Normal file
File diff suppressed because one or more lines are too long
23
frontend/lib/meshcore-decoder/dist/crypto/key-manager.d.ts
vendored
Normal file
23
frontend/lib/meshcore-decoder/dist/crypto/key-manager.d.ts
vendored
Normal file
@@ -0,0 +1,23 @@
|
||||
import { CryptoKeyStore } from '../types/crypto';
|
||||
export declare class MeshCoreKeyStore implements CryptoKeyStore {
|
||||
nodeKeys: Map<string, string>;
|
||||
private channelHashToKeys;
|
||||
constructor(initialKeys?: {
|
||||
channelSecrets?: string[];
|
||||
nodeKeys?: Record<string, string>;
|
||||
});
|
||||
addNodeKey(publicKey: string, privateKey: string): void;
|
||||
hasChannelKey(channelHash: string): boolean;
|
||||
hasNodeKey(publicKey: string): boolean;
|
||||
/**
|
||||
* Get all channel keys that match the given channel hash (handles collisions)
|
||||
*/
|
||||
getChannelKeys(channelHash: string): string[];
|
||||
getNodeKey(publicKey: string): string | undefined;
|
||||
/**
|
||||
* Add channel keys by secret keys (new simplified API)
|
||||
* Automatically calculates channel hashes
|
||||
*/
|
||||
addChannelSecrets(secretKeys: string[]): void;
|
||||
}
|
||||
//# sourceMappingURL=key-manager.d.ts.map
|
||||
1
frontend/lib/meshcore-decoder/dist/crypto/key-manager.d.ts.map
vendored
Normal file
1
frontend/lib/meshcore-decoder/dist/crypto/key-manager.d.ts.map
vendored
Normal file
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"key-manager.d.ts","sourceRoot":"","sources":["../../src/crypto/key-manager.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AAGjD,qBAAa,gBAAiB,YAAW,cAAc;IAC9C,QAAQ,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAa;IAGjD,OAAO,CAAC,iBAAiB,CAA+B;gBAE5C,WAAW,CAAC,EAAE;QACxB,cAAc,CAAC,EAAE,MAAM,EAAE,CAAC;QAC1B,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;KACnC;IAYD,UAAU,CAAC,SAAS,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,IAAI;IAKvD,aAAa,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO;IAK3C,UAAU,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO;IAKtC;;OAEG;IACH,cAAc,CAAC,WAAW,EAAE,MAAM,GAAG,MAAM,EAAE;IAK7C,UAAU,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS;IAKjD;;;OAGG;IACH,iBAAiB,CAAC,UAAU,EAAE,MAAM,EAAE,GAAG,IAAI;CAW9C"}
|
||||
60
frontend/lib/meshcore-decoder/dist/crypto/key-manager.js
vendored
Normal file
60
frontend/lib/meshcore-decoder/dist/crypto/key-manager.js
vendored
Normal file
@@ -0,0 +1,60 @@
|
||||
"use strict";
|
||||
// Copyright (c) 2025 Michael Hart: https://github.com/michaelhart/meshcore-decoder
|
||||
// MIT License
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.MeshCoreKeyStore = void 0;
|
||||
const channel_crypto_1 = require("./channel-crypto");
|
||||
class MeshCoreKeyStore {
|
||||
constructor(initialKeys) {
|
||||
this.nodeKeys = new Map();
|
||||
// internal map for hash -> multiple keys (collision handling)
|
||||
this.channelHashToKeys = new Map();
|
||||
if (initialKeys?.channelSecrets) {
|
||||
this.addChannelSecrets(initialKeys.channelSecrets);
|
||||
}
|
||||
if (initialKeys?.nodeKeys) {
|
||||
Object.entries(initialKeys.nodeKeys).forEach(([pubKey, privKey]) => {
|
||||
this.addNodeKey(pubKey, privKey);
|
||||
});
|
||||
}
|
||||
}
|
||||
addNodeKey(publicKey, privateKey) {
|
||||
const normalizedPubKey = publicKey.toUpperCase();
|
||||
this.nodeKeys.set(normalizedPubKey, privateKey);
|
||||
}
|
||||
hasChannelKey(channelHash) {
|
||||
const normalizedHash = channelHash.toLowerCase();
|
||||
return this.channelHashToKeys.has(normalizedHash);
|
||||
}
|
||||
hasNodeKey(publicKey) {
|
||||
const normalizedPubKey = publicKey.toUpperCase();
|
||||
return this.nodeKeys.has(normalizedPubKey);
|
||||
}
|
||||
/**
|
||||
* Get all channel keys that match the given channel hash (handles collisions)
|
||||
*/
|
||||
getChannelKeys(channelHash) {
|
||||
const normalizedHash = channelHash.toLowerCase();
|
||||
return this.channelHashToKeys.get(normalizedHash) || [];
|
||||
}
|
||||
getNodeKey(publicKey) {
|
||||
const normalizedPubKey = publicKey.toUpperCase();
|
||||
return this.nodeKeys.get(normalizedPubKey);
|
||||
}
|
||||
/**
|
||||
* Add channel keys by secret keys (new simplified API)
|
||||
* Automatically calculates channel hashes
|
||||
*/
|
||||
addChannelSecrets(secretKeys) {
|
||||
for (const secretKey of secretKeys) {
|
||||
const channelHash = channel_crypto_1.ChannelCrypto.calculateChannelHash(secretKey).toLowerCase();
|
||||
// Handle potential hash collisions
|
||||
if (!this.channelHashToKeys.has(channelHash)) {
|
||||
this.channelHashToKeys.set(channelHash, []);
|
||||
}
|
||||
this.channelHashToKeys.get(channelHash).push(secretKey);
|
||||
}
|
||||
}
|
||||
}
|
||||
exports.MeshCoreKeyStore = MeshCoreKeyStore;
|
||||
//# sourceMappingURL=key-manager.js.map
|
||||
1
frontend/lib/meshcore-decoder/dist/crypto/key-manager.js.map
vendored
Normal file
1
frontend/lib/meshcore-decoder/dist/crypto/key-manager.js.map
vendored
Normal file
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"key-manager.js","sourceRoot":"","sources":["../../src/crypto/key-manager.ts"],"names":[],"mappings":";AAAA,mFAAmF;AACnF,cAAc;;;AAGd,qDAAiD;AAEjD,MAAa,gBAAgB;IAM3B,YAAY,WAGX;QARM,aAAQ,GAAwB,IAAI,GAAG,EAAE,CAAC;QAEjD,8DAA8D;QACtD,sBAAiB,GAAG,IAAI,GAAG,EAAoB,CAAC;QAMtD,IAAI,WAAW,EAAE,cAAc,EAAE,CAAC;YAChC,IAAI,CAAC,iBAAiB,CAAC,WAAW,CAAC,cAAc,CAAC,CAAC;QACrD,CAAC;QAED,IAAI,WAAW,EAAE,QAAQ,EAAE,CAAC;YAC1B,MAAM,CAAC,OAAO,CAAC,WAAW,CAAC,QAAQ,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,EAAE;gBACjE,IAAI,CAAC,UAAU,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;YACnC,CAAC,CAAC,CAAC;QACL,CAAC;IACH,CAAC;IAED,UAAU,CAAC,SAAiB,EAAE,UAAkB;QAC9C,MAAM,gBAAgB,GAAG,SAAS,CAAC,WAAW,EAAE,CAAC;QACjD,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,gBAAgB,EAAE,UAAU,CAAC,CAAC;IAClD,CAAC;IAED,aAAa,CAAC,WAAmB;QAC/B,MAAM,cAAc,GAAG,WAAW,CAAC,WAAW,EAAE,CAAC;QACjD,OAAO,IAAI,CAAC,iBAAiB,CAAC,GAAG,CAAC,cAAc,CAAC,CAAC;IACpD,CAAC;IAED,UAAU,CAAC,SAAiB;QAC1B,MAAM,gBAAgB,GAAG,SAAS,CAAC,WAAW,EAAE,CAAC;QACjD,OAAO,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,gBAAgB,CAAC,CAAC;IAC7C,CAAC;IAED;;OAEG;IACH,cAAc,CAAC,WAAmB;QAChC,MAAM,cAAc,GAAG,WAAW,CAAC,WAAW,EAAE,CAAC;QACjD,OAAO,IAAI,CAAC,iBAAiB,CAAC,GAAG,CAAC,cAAc,CAAC,IAAI,EAAE,CAAC;IAC1D,CAAC;IAED,UAAU,CAAC,SAAiB;QAC1B,MAAM,gBAAgB,GAAG,SAAS,CAAC,WAAW,EAAE,CAAC;QACjD,OAAO,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,gBAAgB,CAAC,CAAC;IAC7C,CAAC;IAED;;;OAGG;IACH,iBAAiB,CAAC,UAAoB;QACpC,KAAK,MAAM,SAAS,IAAI,UAAU,EAAE,CAAC;YACnC,MAAM,WAAW,GAAG,8BAAa,CAAC,oBAAoB,CAAC,SAAS,CAAC,CAAC,WAAW,EAAE,CAAC;YAEhF,mCAAmC;YACnC,IAAI,CAAC,IAAI,CAAC,iBAAiB,CAAC,GAAG,CAAC,WAAW,CAAC,EAAE,CAAC;gBAC7C,IAAI,CAAC,iBAAiB,CAAC,GAAG,CAAC,WAAW,EAAE,EAAE,CAAC,CAAC;YAC9C,CAAC;YACD,IAAI,CAAC,iBAAiB,CAAC,GAAG,CAAC,WAAW,CAAE,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;QAC3D,CAAC;IACH,CAAC;CACF;AAhED,4CAgEC"}
|
||||
34
frontend/lib/meshcore-decoder/dist/crypto/orlp-ed25519-wasm.d.ts
vendored
Normal file
34
frontend/lib/meshcore-decoder/dist/crypto/orlp-ed25519-wasm.d.ts
vendored
Normal file
@@ -0,0 +1,34 @@
|
||||
/**
|
||||
* Derive Ed25519 public key from private key using the exact orlp/ed25519 algorithm
|
||||
*
|
||||
* @param privateKeyHex - 64-byte private key in hex format (orlp/ed25519 format)
|
||||
* @returns 32-byte public key in hex format
|
||||
*/
|
||||
export declare function derivePublicKey(privateKeyHex: string): Promise<string>;
|
||||
/**
|
||||
* Validate that a private key and public key pair match using orlp/ed25519
|
||||
*
|
||||
* @param privateKeyHex - 64-byte private key in hex format
|
||||
* @param expectedPublicKeyHex - 32-byte public key in hex format
|
||||
* @returns true if the keys match, false otherwise
|
||||
*/
|
||||
export declare function validateKeyPair(privateKeyHex: string, expectedPublicKeyHex: string): Promise<boolean>;
|
||||
/**
|
||||
* Sign a message using Ed25519 with orlp/ed25519 implementation
|
||||
*
|
||||
* @param messageHex - Message to sign in hex format
|
||||
* @param privateKeyHex - 64-byte private key in hex format (orlp/ed25519 format)
|
||||
* @param publicKeyHex - 32-byte public key in hex format
|
||||
* @returns 64-byte signature in hex format
|
||||
*/
|
||||
export declare function sign(messageHex: string, privateKeyHex: string, publicKeyHex: string): Promise<string>;
|
||||
/**
|
||||
* Verify an Ed25519 signature using orlp/ed25519 implementation
|
||||
*
|
||||
* @param signatureHex - 64-byte signature in hex format
|
||||
* @param messageHex - Message that was signed in hex format
|
||||
* @param publicKeyHex - 32-byte public key in hex format
|
||||
* @returns true if signature is valid, false otherwise
|
||||
*/
|
||||
export declare function verify(signatureHex: string, messageHex: string, publicKeyHex: string): Promise<boolean>;
|
||||
//# sourceMappingURL=orlp-ed25519-wasm.d.ts.map
|
||||
1
frontend/lib/meshcore-decoder/dist/crypto/orlp-ed25519-wasm.d.ts.map
vendored
Normal file
1
frontend/lib/meshcore-decoder/dist/crypto/orlp-ed25519-wasm.d.ts.map
vendored
Normal file
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"orlp-ed25519-wasm.d.ts","sourceRoot":"","sources":["../../src/crypto/orlp-ed25519-wasm.ts"],"names":[],"mappings":"AAgBA;;;;;GAKG;AACH,wBAAsB,eAAe,CAAC,aAAa,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAiC5E;AAED;;;;;;GAMG;AACH,wBAAsB,eAAe,CAAC,aAAa,EAAE,MAAM,EAAE,oBAAoB,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAoC3G;AAED;;;;;;;GAOG;AACH,wBAAsB,IAAI,CAAC,UAAU,EAAE,MAAM,EAAE,aAAa,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAuC3G;AAED;;;;;;;GAOG;AACH,wBAAsB,MAAM,CAAC,YAAY,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAsC7G"}
|
||||
150
frontend/lib/meshcore-decoder/dist/crypto/orlp-ed25519-wasm.js
vendored
Normal file
150
frontend/lib/meshcore-decoder/dist/crypto/orlp-ed25519-wasm.js
vendored
Normal file
@@ -0,0 +1,150 @@
|
||||
"use strict";
|
||||
// WebAssembly wrapper for orlp/ed25519 key derivation
|
||||
// This provides the exact orlp algorithm for JavaScript
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.derivePublicKey = derivePublicKey;
|
||||
exports.validateKeyPair = validateKeyPair;
|
||||
exports.sign = sign;
|
||||
exports.verify = verify;
|
||||
const hex_1 = require("../utils/hex");
|
||||
// Import the generated WASM module
|
||||
const OrlpEd25519 = require('../../lib/orlp-ed25519.js');
|
||||
/**
|
||||
* Get a fresh WASM instance
|
||||
* Loads a fresh instance each time because the WASM module could behave unpredictably otherwise
|
||||
*/
|
||||
async function getWasmInstance() {
|
||||
return await OrlpEd25519();
|
||||
}
|
||||
/**
|
||||
* Derive Ed25519 public key from private key using the exact orlp/ed25519 algorithm
|
||||
*
|
||||
* @param privateKeyHex - 64-byte private key in hex format (orlp/ed25519 format)
|
||||
* @returns 32-byte public key in hex format
|
||||
*/
|
||||
async function derivePublicKey(privateKeyHex) {
|
||||
const wasmModule = await getWasmInstance();
|
||||
const privateKeyBytes = (0, hex_1.hexToBytes)(privateKeyHex);
|
||||
if (privateKeyBytes.length !== 64) {
|
||||
throw new Error(`Invalid private key length: expected 64 bytes, got ${privateKeyBytes.length}`);
|
||||
}
|
||||
// Allocate memory buffers directly in WASM heap
|
||||
const privateKeyPtr = 1024; // Use fixed memory locations
|
||||
const publicKeyPtr = 1024 + 64;
|
||||
// Copy private key to WASM memory
|
||||
wasmModule.HEAPU8.set(privateKeyBytes, privateKeyPtr);
|
||||
// Call the orlp key derivation function
|
||||
const result = wasmModule.ccall('orlp_derive_public_key', 'number', ['number', 'number'], [publicKeyPtr, privateKeyPtr]);
|
||||
if (result !== 0) {
|
||||
throw new Error('orlp key derivation failed: invalid private key');
|
||||
}
|
||||
// Read the public key from WASM memory
|
||||
const publicKeyBytes = new Uint8Array(32);
|
||||
publicKeyBytes.set(wasmModule.HEAPU8.subarray(publicKeyPtr, publicKeyPtr + 32));
|
||||
return (0, hex_1.bytesToHex)(publicKeyBytes);
|
||||
}
|
||||
/**
|
||||
* Validate that a private key and public key pair match using orlp/ed25519
|
||||
*
|
||||
* @param privateKeyHex - 64-byte private key in hex format
|
||||
* @param expectedPublicKeyHex - 32-byte public key in hex format
|
||||
* @returns true if the keys match, false otherwise
|
||||
*/
|
||||
async function validateKeyPair(privateKeyHex, expectedPublicKeyHex) {
|
||||
try {
|
||||
const wasmModule = await getWasmInstance();
|
||||
const privateKeyBytes = (0, hex_1.hexToBytes)(privateKeyHex);
|
||||
const expectedPublicKeyBytes = (0, hex_1.hexToBytes)(expectedPublicKeyHex);
|
||||
if (privateKeyBytes.length !== 64) {
|
||||
return false;
|
||||
}
|
||||
if (expectedPublicKeyBytes.length !== 32) {
|
||||
return false;
|
||||
}
|
||||
// Allocate memory buffers directly in WASM heap
|
||||
const privateKeyPtr = 2048; // Use different fixed memory locations
|
||||
const publicKeyPtr = 2048 + 64;
|
||||
// Copy keys to WASM memory
|
||||
wasmModule.HEAPU8.set(privateKeyBytes, privateKeyPtr);
|
||||
wasmModule.HEAPU8.set(expectedPublicKeyBytes, publicKeyPtr);
|
||||
// Call the validation function (note: C function expects public_key first, then private_key)
|
||||
const result = wasmModule.ccall('orlp_validate_keypair', 'number', ['number', 'number'], [publicKeyPtr, privateKeyPtr]);
|
||||
return result === 1;
|
||||
}
|
||||
catch (error) {
|
||||
// Invalid hex strings or other errors should return false
|
||||
return false;
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Sign a message using Ed25519 with orlp/ed25519 implementation
|
||||
*
|
||||
* @param messageHex - Message to sign in hex format
|
||||
* @param privateKeyHex - 64-byte private key in hex format (orlp/ed25519 format)
|
||||
* @param publicKeyHex - 32-byte public key in hex format
|
||||
* @returns 64-byte signature in hex format
|
||||
*/
|
||||
async function sign(messageHex, privateKeyHex, publicKeyHex) {
|
||||
const wasmModule = await getWasmInstance();
|
||||
const messageBytes = (0, hex_1.hexToBytes)(messageHex);
|
||||
const privateKeyBytes = (0, hex_1.hexToBytes)(privateKeyHex);
|
||||
const publicKeyBytes = (0, hex_1.hexToBytes)(publicKeyHex);
|
||||
if (privateKeyBytes.length !== 64) {
|
||||
throw new Error(`Invalid private key length: expected 64 bytes, got ${privateKeyBytes.length}`);
|
||||
}
|
||||
if (publicKeyBytes.length !== 32) {
|
||||
throw new Error(`Invalid public key length: expected 32 bytes, got ${publicKeyBytes.length}`);
|
||||
}
|
||||
// Allocate memory buffers with large gaps to avoid conflicts with scratch space
|
||||
const messagePtr = 100000;
|
||||
const privateKeyPtr = 200000;
|
||||
const publicKeyPtr = 300000;
|
||||
const signaturePtr = 400000;
|
||||
// Copy data to WASM memory
|
||||
wasmModule.HEAPU8.set(messageBytes, messagePtr);
|
||||
wasmModule.HEAPU8.set(privateKeyBytes, privateKeyPtr);
|
||||
wasmModule.HEAPU8.set(publicKeyBytes, publicKeyPtr);
|
||||
// Call orlp_sign
|
||||
wasmModule.ccall('orlp_sign', 'void', ['number', 'number', 'number', 'number', 'number'], [signaturePtr, messagePtr, messageBytes.length, publicKeyPtr, privateKeyPtr]);
|
||||
// Read signature
|
||||
const signatureBytes = new Uint8Array(64);
|
||||
signatureBytes.set(wasmModule.HEAPU8.subarray(signaturePtr, signaturePtr + 64));
|
||||
return (0, hex_1.bytesToHex)(signatureBytes);
|
||||
}
|
||||
/**
|
||||
* Verify an Ed25519 signature using orlp/ed25519 implementation
|
||||
*
|
||||
* @param signatureHex - 64-byte signature in hex format
|
||||
* @param messageHex - Message that was signed in hex format
|
||||
* @param publicKeyHex - 32-byte public key in hex format
|
||||
* @returns true if signature is valid, false otherwise
|
||||
*/
|
||||
async function verify(signatureHex, messageHex, publicKeyHex) {
|
||||
try {
|
||||
const wasmModule = await getWasmInstance();
|
||||
const signatureBytes = (0, hex_1.hexToBytes)(signatureHex);
|
||||
const messageBytes = (0, hex_1.hexToBytes)(messageHex);
|
||||
const publicKeyBytes = (0, hex_1.hexToBytes)(publicKeyHex);
|
||||
if (signatureBytes.length !== 64) {
|
||||
return false;
|
||||
}
|
||||
if (publicKeyBytes.length !== 32) {
|
||||
return false;
|
||||
}
|
||||
// Allocate memory buffers with large gaps to avoid conflicts with scratch space
|
||||
const messagePtr = 500000;
|
||||
const signaturePtr = 600000;
|
||||
const publicKeyPtr = 700000;
|
||||
// Copy data to WASM memory
|
||||
wasmModule.HEAPU8.set(signatureBytes, signaturePtr);
|
||||
wasmModule.HEAPU8.set(messageBytes, messagePtr);
|
||||
wasmModule.HEAPU8.set(publicKeyBytes, publicKeyPtr);
|
||||
// Call the orlp verify function
|
||||
const result = wasmModule.ccall('orlp_verify', 'number', ['number', 'number', 'number', 'number'], [signaturePtr, messagePtr, messageBytes.length, publicKeyPtr]);
|
||||
return result === 1;
|
||||
}
|
||||
catch (error) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
//# sourceMappingURL=orlp-ed25519-wasm.js.map
|
||||
1
frontend/lib/meshcore-decoder/dist/crypto/orlp-ed25519-wasm.js.map
vendored
Normal file
1
frontend/lib/meshcore-decoder/dist/crypto/orlp-ed25519-wasm.js.map
vendored
Normal file
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"orlp-ed25519-wasm.js","sourceRoot":"","sources":["../../src/crypto/orlp-ed25519-wasm.ts"],"names":[],"mappings":"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"}
|
||||
51
frontend/lib/meshcore-decoder/dist/decoder/packet-decoder.d.ts
vendored
Normal file
51
frontend/lib/meshcore-decoder/dist/decoder/packet-decoder.d.ts
vendored
Normal file
@@ -0,0 +1,51 @@
|
||||
import { DecodedPacket, PacketStructure } from '../types/packet';
|
||||
import { DecryptionOptions, ValidationResult, CryptoKeyStore } from '../types/crypto';
|
||||
export declare class MeshCorePacketDecoder {
|
||||
/**
|
||||
* Decode a raw packet from hex string
|
||||
*/
|
||||
static decode(hexData: string, options?: DecryptionOptions): DecodedPacket;
|
||||
/**
|
||||
* Decode a raw packet from hex string with signature verification for advertisements
|
||||
*/
|
||||
static decodeWithVerification(hexData: string, options?: DecryptionOptions): Promise<DecodedPacket>;
|
||||
/**
|
||||
* Analyze packet structure for detailed breakdown
|
||||
*/
|
||||
static analyzeStructure(hexData: string, options?: DecryptionOptions): PacketStructure;
|
||||
/**
|
||||
* Analyze packet structure for detailed breakdown with signature verification for advertisements
|
||||
*/
|
||||
static analyzeStructureWithVerification(hexData: string, options?: DecryptionOptions): Promise<PacketStructure>;
|
||||
/**
|
||||
* Internal unified parsing method
|
||||
*/
|
||||
private static parseInternal;
|
||||
/**
|
||||
* Internal unified parsing method with signature verification for advertisements
|
||||
*/
|
||||
private static parseInternalAsync;
|
||||
/**
|
||||
* Validate packet format without full decoding
|
||||
*/
|
||||
static validate(hexData: string): ValidationResult;
|
||||
/**
|
||||
* Calculate message hash for a packet
|
||||
*/
|
||||
static calculateMessageHash(bytes: Uint8Array, routeType: number, payloadType: number, payloadVersion: number): string;
|
||||
/**
|
||||
* Create a key store for decryption
|
||||
*/
|
||||
static createKeyStore(initialKeys?: {
|
||||
channelSecrets?: string[];
|
||||
nodeKeys?: Record<string, string>;
|
||||
}): CryptoKeyStore;
|
||||
/**
|
||||
* Decode a path_len byte into hash size, hop count, and total byte length.
|
||||
* Firmware reference: Packet.h lines 79-83
|
||||
* Bits 7:6 = hash size selector: (path_len >> 6) + 1 = 1, 2, or 3 bytes per hop
|
||||
* Bits 5:0 = hop count (0-63)
|
||||
*/
|
||||
private static decodePathLenByte;
|
||||
}
|
||||
//# sourceMappingURL=packet-decoder.d.ts.map
|
||||
1
frontend/lib/meshcore-decoder/dist/decoder/packet-decoder.d.ts.map
vendored
Normal file
1
frontend/lib/meshcore-decoder/dist/decoder/packet-decoder.d.ts.map
vendored
Normal file
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"packet-decoder.d.ts","sourceRoot":"","sources":["../../src/decoder/packet-decoder.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,aAAa,EAAE,eAAe,EAAkD,MAAM,iBAAiB,CAAC;AAIjH,OAAO,EAAE,iBAAiB,EAAE,gBAAgB,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AAatF,qBAAa,qBAAqB;IAChC;;OAEG;IACH,MAAM,CAAC,MAAM,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,iBAAiB,GAAG,aAAa;IAK1E;;OAEG;WACU,sBAAsB,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,iBAAiB,GAAG,OAAO,CAAC,aAAa,CAAC;IAKzG;;OAEG;IACH,MAAM,CAAC,gBAAgB,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,iBAAiB,GAAG,eAAe;IAKtF;;OAEG;WACU,gCAAgC,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,iBAAiB,GAAG,OAAO,CAAC,eAAe,CAAC;IAKrH;;OAEG;IACH,OAAO,CAAC,MAAM,CAAC,aAAa;IA6Y5B;;OAEG;mBACkB,kBAAkB;IA2CvC;;OAEG;IACH,MAAM,CAAC,QAAQ,CAAC,OAAO,EAAE,MAAM,GAAG,gBAAgB;IAmDlD;;OAEG;IACH,MAAM,CAAC,oBAAoB,CAAC,KAAK,EAAE,UAAU,EAAE,SAAS,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,EAAE,cAAc,EAAE,MAAM,GAAG,MAAM;IAkDtH;;OAEG;IACH,MAAM,CAAC,cAAc,CAAC,WAAW,CAAC,EAAE;QAClC,cAAc,CAAC,EAAE,MAAM,EAAE,CAAC;QAC1B,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;KACnC,GAAG,cAAc;IAIlB;;;;;OAKG;IACH,OAAO,CAAC,MAAM,CAAC,iBAAiB;CAMjC"}
|
||||
576
frontend/lib/meshcore-decoder/dist/decoder/packet-decoder.js
vendored
Normal file
576
frontend/lib/meshcore-decoder/dist/decoder/packet-decoder.js
vendored
Normal file
@@ -0,0 +1,576 @@
|
||||
"use strict";
|
||||
// Copyright (c) 2025 Michael Hart: https://github.com/michaelhart/meshcore-decoder
|
||||
// MIT License
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.MeshCorePacketDecoder = void 0;
|
||||
const enums_1 = require("../types/enums");
|
||||
const hex_1 = require("../utils/hex");
|
||||
const enum_names_1 = require("../utils/enum-names");
|
||||
const key_manager_1 = require("../crypto/key-manager");
|
||||
const advert_1 = require("./payload-decoders/advert");
|
||||
const trace_1 = require("./payload-decoders/trace");
|
||||
const group_text_1 = require("./payload-decoders/group-text");
|
||||
const request_1 = require("./payload-decoders/request");
|
||||
const response_1 = require("./payload-decoders/response");
|
||||
const anon_request_1 = require("./payload-decoders/anon-request");
|
||||
const ack_1 = require("./payload-decoders/ack");
|
||||
const path_1 = require("./payload-decoders/path");
|
||||
const text_message_1 = require("./payload-decoders/text-message");
|
||||
const control_1 = require("./payload-decoders/control");
|
||||
class MeshCorePacketDecoder {
|
||||
/**
|
||||
* Decode a raw packet from hex string
|
||||
*/
|
||||
static decode(hexData, options) {
|
||||
const result = this.parseInternal(hexData, false, options);
|
||||
return result.packet;
|
||||
}
|
||||
/**
|
||||
* Decode a raw packet from hex string with signature verification for advertisements
|
||||
*/
|
||||
static async decodeWithVerification(hexData, options) {
|
||||
const result = await this.parseInternalAsync(hexData, false, options);
|
||||
return result.packet;
|
||||
}
|
||||
/**
|
||||
* Analyze packet structure for detailed breakdown
|
||||
*/
|
||||
static analyzeStructure(hexData, options) {
|
||||
const result = this.parseInternal(hexData, true, options);
|
||||
return result.structure;
|
||||
}
|
||||
/**
|
||||
* Analyze packet structure for detailed breakdown with signature verification for advertisements
|
||||
*/
|
||||
static async analyzeStructureWithVerification(hexData, options) {
|
||||
const result = await this.parseInternalAsync(hexData, true, options);
|
||||
return result.structure;
|
||||
}
|
||||
/**
|
||||
* Internal unified parsing method
|
||||
*/
|
||||
static parseInternal(hexData, includeStructure, options) {
|
||||
const bytes = (0, hex_1.hexToBytes)(hexData);
|
||||
const segments = [];
|
||||
if (bytes.length < 2) {
|
||||
const errorPacket = {
|
||||
messageHash: '',
|
||||
routeType: enums_1.RouteType.Flood,
|
||||
payloadType: enums_1.PayloadType.RawCustom,
|
||||
payloadVersion: enums_1.PayloadVersion.Version1,
|
||||
pathLength: 0,
|
||||
path: null,
|
||||
payload: { raw: '', decoded: null },
|
||||
totalBytes: bytes.length,
|
||||
isValid: false,
|
||||
errors: ['Packet too short (minimum 2 bytes required)']
|
||||
};
|
||||
const errorStructure = {
|
||||
segments: [],
|
||||
totalBytes: bytes.length,
|
||||
rawHex: hexData.toUpperCase(),
|
||||
messageHash: '',
|
||||
payload: {
|
||||
segments: [],
|
||||
hex: '',
|
||||
startByte: 0,
|
||||
type: 'Unknown'
|
||||
}
|
||||
};
|
||||
return { packet: errorPacket, structure: errorStructure };
|
||||
}
|
||||
try {
|
||||
let offset = 0;
|
||||
// parse header
|
||||
const header = bytes[0];
|
||||
const routeType = header & 0x03;
|
||||
const payloadType = (header >> 2) & 0x0F;
|
||||
const payloadVersion = (header >> 6) & 0x03;
|
||||
if (includeStructure) {
|
||||
segments.push({
|
||||
name: 'Header',
|
||||
description: 'Header byte breakdown',
|
||||
startByte: 0,
|
||||
endByte: 0,
|
||||
value: `0x${header.toString(16).padStart(2, '0')}`,
|
||||
headerBreakdown: {
|
||||
fullBinary: header.toString(2).padStart(8, '0'),
|
||||
fields: [
|
||||
{
|
||||
bits: '0-1',
|
||||
field: 'Route Type',
|
||||
value: (0, enum_names_1.getRouteTypeName)(routeType),
|
||||
binary: (header & 0x03).toString(2).padStart(2, '0')
|
||||
},
|
||||
{
|
||||
bits: '2-5',
|
||||
field: 'Payload Type',
|
||||
value: (0, enum_names_1.getPayloadTypeName)(payloadType),
|
||||
binary: ((header >> 2) & 0x0F).toString(2).padStart(4, '0')
|
||||
},
|
||||
{
|
||||
bits: '6-7',
|
||||
field: 'Version',
|
||||
value: payloadVersion.toString(),
|
||||
binary: ((header >> 6) & 0x03).toString(2).padStart(2, '0')
|
||||
}
|
||||
]
|
||||
}
|
||||
});
|
||||
}
|
||||
offset = 1;
|
||||
// handle transport codes
|
||||
let transportCodes;
|
||||
if (routeType === enums_1.RouteType.TransportFlood || routeType === enums_1.RouteType.TransportDirect) {
|
||||
if (bytes.length < offset + 4) {
|
||||
throw new Error('Packet too short for transport codes');
|
||||
}
|
||||
const code1 = bytes[offset] | (bytes[offset + 1] << 8);
|
||||
const code2 = bytes[offset + 2] | (bytes[offset + 3] << 8);
|
||||
transportCodes = [code1, code2];
|
||||
if (includeStructure) {
|
||||
const transportCode = (bytes[offset]) | (bytes[offset + 1] << 8) | (bytes[offset + 2] << 16) | (bytes[offset + 3] << 24);
|
||||
segments.push({
|
||||
name: 'Transport Code',
|
||||
description: 'Used for Direct/Response routing',
|
||||
startByte: offset,
|
||||
endByte: offset + 3,
|
||||
value: `0x${transportCode.toString(16).padStart(8, '0')}`
|
||||
});
|
||||
}
|
||||
offset += 4;
|
||||
}
|
||||
// parse path length byte (encodes hash size and hop count)
|
||||
// Bits 7:6 = hash size selector: (path_len >> 6) + 1 = 1, 2, or 3 bytes per hop
|
||||
// Bits 5:0 = hop count (0-63)
|
||||
if (bytes.length < offset + 1) {
|
||||
throw new Error('Packet too short for path length');
|
||||
}
|
||||
const pathLenByte = bytes[offset];
|
||||
const { hashSize: pathHashSize, hopCount: pathHopCount, byteLength: pathByteLength } = this.decodePathLenByte(pathLenByte);
|
||||
if (pathHashSize === 4) {
|
||||
throw new Error('Invalid path length byte: reserved hash size (bits 7:6 = 11)');
|
||||
}
|
||||
if (includeStructure) {
|
||||
const hashDesc = pathHashSize > 1 ? ` × ${pathHashSize}-byte hashes (${pathByteLength} bytes)` : '';
|
||||
let pathLengthDescription;
|
||||
if (pathHopCount === 0) {
|
||||
pathLengthDescription = pathHashSize > 1 ? `No path data (${pathHashSize}-byte hash mode)` : 'No path data';
|
||||
}
|
||||
else if (routeType === enums_1.RouteType.Direct || routeType === enums_1.RouteType.TransportDirect) {
|
||||
pathLengthDescription = `${pathHopCount} hops${hashDesc} of routing instructions (decreases as packet travels)`;
|
||||
}
|
||||
else if (routeType === enums_1.RouteType.Flood || routeType === enums_1.RouteType.TransportFlood) {
|
||||
pathLengthDescription = `${pathHopCount} hops${hashDesc} showing route taken (increases as packet floods)`;
|
||||
}
|
||||
else {
|
||||
pathLengthDescription = `Path contains ${pathHopCount} hops${hashDesc}`;
|
||||
}
|
||||
segments.push({
|
||||
name: 'Path Length',
|
||||
description: pathLengthDescription,
|
||||
startByte: offset,
|
||||
endByte: offset,
|
||||
value: `0x${pathLenByte.toString(16).padStart(2, '0')}`,
|
||||
headerBreakdown: {
|
||||
fullBinary: pathLenByte.toString(2).padStart(8, '0'),
|
||||
fields: [
|
||||
{
|
||||
bits: '6-7',
|
||||
field: 'Hash Size',
|
||||
value: `${pathHashSize} byte${pathHashSize > 1 ? 's' : ''} per hop`,
|
||||
binary: ((pathLenByte >> 6) & 0x03).toString(2).padStart(2, '0')
|
||||
},
|
||||
{
|
||||
bits: '0-5',
|
||||
field: 'Hop Count',
|
||||
value: `${pathHopCount} hop${pathHopCount !== 1 ? 's' : ''}`,
|
||||
binary: (pathLenByte & 63).toString(2).padStart(6, '0')
|
||||
}
|
||||
]
|
||||
}
|
||||
});
|
||||
}
|
||||
offset += 1;
|
||||
if (bytes.length < offset + pathByteLength) {
|
||||
throw new Error('Packet too short for path data');
|
||||
}
|
||||
// convert path data to grouped hex strings (one entry per hop)
|
||||
const pathBytes = bytes.subarray(offset, offset + pathByteLength);
|
||||
let path = null;
|
||||
if (pathHopCount > 0) {
|
||||
path = [];
|
||||
for (let i = 0; i < pathHopCount; i++) {
|
||||
const hopBytes = pathBytes.subarray(i * pathHashSize, (i + 1) * pathHashSize);
|
||||
path.push((0, hex_1.bytesToHex)(hopBytes));
|
||||
}
|
||||
}
|
||||
if (includeStructure && pathHopCount > 0) {
|
||||
if (payloadType === enums_1.PayloadType.Trace) {
|
||||
// TRACE packets have SNR values in path (always single-byte entries)
|
||||
const snrValues = [];
|
||||
for (let i = 0; i < pathByteLength; i++) {
|
||||
const snrRaw = bytes[offset + i];
|
||||
const snrSigned = snrRaw > 127 ? snrRaw - 256 : snrRaw;
|
||||
const snrDb = snrSigned / 4.0;
|
||||
snrValues.push(`${snrDb.toFixed(2)}dB (0x${snrRaw.toString(16).padStart(2, '0')})`);
|
||||
}
|
||||
segments.push({
|
||||
name: 'Path SNR Data',
|
||||
description: `SNR values collected during trace: ${snrValues.join(', ')}`,
|
||||
startByte: offset,
|
||||
endByte: offset + pathByteLength - 1,
|
||||
value: (0, hex_1.bytesToHex)(bytes.slice(offset, offset + pathByteLength))
|
||||
});
|
||||
}
|
||||
else {
|
||||
let pathDescription = 'Routing path information';
|
||||
if (routeType === enums_1.RouteType.Direct || routeType === enums_1.RouteType.TransportDirect) {
|
||||
pathDescription = `Routing instructions (${pathHashSize}-byte hashes stripped at each hop as packet travels to destination)`;
|
||||
}
|
||||
else if (routeType === enums_1.RouteType.Flood || routeType === enums_1.RouteType.TransportFlood) {
|
||||
pathDescription = `Historical route taken (${pathHashSize}-byte hashes added as packet floods through network)`;
|
||||
}
|
||||
segments.push({
|
||||
name: 'Path Data',
|
||||
description: pathDescription,
|
||||
startByte: offset,
|
||||
endByte: offset + pathByteLength - 1,
|
||||
value: (0, hex_1.bytesToHex)(bytes.slice(offset, offset + pathByteLength))
|
||||
});
|
||||
}
|
||||
}
|
||||
offset += pathByteLength;
|
||||
// extract payload
|
||||
const payloadBytes = bytes.subarray(offset);
|
||||
const payloadHex = (0, hex_1.bytesToHex)(payloadBytes);
|
||||
if (includeStructure && bytes.length > offset) {
|
||||
segments.push({
|
||||
name: 'Payload',
|
||||
description: `${(0, enum_names_1.getPayloadTypeName)(payloadType)} payload data`,
|
||||
startByte: offset,
|
||||
endByte: bytes.length - 1,
|
||||
value: (0, hex_1.bytesToHex)(bytes.slice(offset))
|
||||
});
|
||||
}
|
||||
// decode payload based on type and optionally get segments in one pass
|
||||
let decodedPayload = null;
|
||||
const payloadSegments = [];
|
||||
if (payloadType === enums_1.PayloadType.Advert) {
|
||||
const result = advert_1.AdvertPayloadDecoder.decode(payloadBytes, {
|
||||
includeSegments: includeStructure,
|
||||
segmentOffset: 0
|
||||
});
|
||||
decodedPayload = result;
|
||||
if (result?.segments) {
|
||||
payloadSegments.push(...result.segments);
|
||||
delete result.segments;
|
||||
}
|
||||
}
|
||||
else if (payloadType === enums_1.PayloadType.Trace) {
|
||||
const result = trace_1.TracePayloadDecoder.decode(payloadBytes, path, {
|
||||
includeSegments: includeStructure,
|
||||
segmentOffset: 0 // Payload segments are relative to payload start
|
||||
});
|
||||
decodedPayload = result;
|
||||
if (result?.segments) {
|
||||
payloadSegments.push(...result.segments);
|
||||
delete result.segments; // Remove from decoded payload to keep it clean
|
||||
}
|
||||
}
|
||||
else if (payloadType === enums_1.PayloadType.GroupText) {
|
||||
const result = group_text_1.GroupTextPayloadDecoder.decode(payloadBytes, {
|
||||
...options,
|
||||
includeSegments: includeStructure,
|
||||
segmentOffset: 0
|
||||
});
|
||||
decodedPayload = result;
|
||||
if (result?.segments) {
|
||||
payloadSegments.push(...result.segments);
|
||||
delete result.segments;
|
||||
}
|
||||
}
|
||||
else if (payloadType === enums_1.PayloadType.Request) {
|
||||
const result = request_1.RequestPayloadDecoder.decode(payloadBytes, {
|
||||
includeSegments: includeStructure,
|
||||
segmentOffset: 0 // Payload segments are relative to payload start
|
||||
});
|
||||
decodedPayload = result;
|
||||
if (result?.segments) {
|
||||
payloadSegments.push(...result.segments);
|
||||
delete result.segments;
|
||||
}
|
||||
}
|
||||
else if (payloadType === enums_1.PayloadType.Response) {
|
||||
const result = response_1.ResponsePayloadDecoder.decode(payloadBytes, {
|
||||
includeSegments: includeStructure,
|
||||
segmentOffset: 0 // Payload segments are relative to payload start
|
||||
});
|
||||
decodedPayload = result;
|
||||
if (result?.segments) {
|
||||
payloadSegments.push(...result.segments);
|
||||
delete result.segments;
|
||||
}
|
||||
}
|
||||
else if (payloadType === enums_1.PayloadType.AnonRequest) {
|
||||
const result = anon_request_1.AnonRequestPayloadDecoder.decode(payloadBytes, {
|
||||
includeSegments: includeStructure,
|
||||
segmentOffset: 0
|
||||
});
|
||||
decodedPayload = result;
|
||||
if (result?.segments) {
|
||||
payloadSegments.push(...result.segments);
|
||||
delete result.segments;
|
||||
}
|
||||
}
|
||||
else if (payloadType === enums_1.PayloadType.Ack) {
|
||||
const result = ack_1.AckPayloadDecoder.decode(payloadBytes, {
|
||||
includeSegments: includeStructure,
|
||||
segmentOffset: 0
|
||||
});
|
||||
decodedPayload = result;
|
||||
if (result?.segments) {
|
||||
payloadSegments.push(...result.segments);
|
||||
delete result.segments;
|
||||
}
|
||||
}
|
||||
else if (payloadType === enums_1.PayloadType.Path) {
|
||||
decodedPayload = path_1.PathPayloadDecoder.decode(payloadBytes);
|
||||
}
|
||||
else if (payloadType === enums_1.PayloadType.TextMessage) {
|
||||
const result = text_message_1.TextMessagePayloadDecoder.decode(payloadBytes, {
|
||||
includeSegments: includeStructure,
|
||||
segmentOffset: 0
|
||||
});
|
||||
decodedPayload = result;
|
||||
if (result?.segments) {
|
||||
payloadSegments.push(...result.segments);
|
||||
delete result.segments;
|
||||
}
|
||||
}
|
||||
else if (payloadType === enums_1.PayloadType.Control) {
|
||||
const result = control_1.ControlPayloadDecoder.decode(payloadBytes, {
|
||||
includeSegments: includeStructure,
|
||||
segmentOffset: 0
|
||||
});
|
||||
decodedPayload = result;
|
||||
if (result?.segments) {
|
||||
payloadSegments.push(...result.segments);
|
||||
delete result.segments;
|
||||
}
|
||||
}
|
||||
// if no segments were generated and we need structure, show basic payload info
|
||||
if (includeStructure && payloadSegments.length === 0 && bytes.length > offset) {
|
||||
payloadSegments.push({
|
||||
name: `${(0, enum_names_1.getPayloadTypeName)(payloadType)} Payload`,
|
||||
description: `Raw ${(0, enum_names_1.getPayloadTypeName)(payloadType)} payload data (${payloadBytes.length} bytes)`,
|
||||
startByte: 0,
|
||||
endByte: payloadBytes.length - 1,
|
||||
value: (0, hex_1.bytesToHex)(payloadBytes)
|
||||
});
|
||||
}
|
||||
// calculate message hash
|
||||
const messageHash = this.calculateMessageHash(bytes, routeType, payloadType, payloadVersion);
|
||||
const packet = {
|
||||
messageHash,
|
||||
routeType,
|
||||
payloadType,
|
||||
payloadVersion,
|
||||
transportCodes,
|
||||
pathLength: pathHopCount,
|
||||
...(pathHashSize > 1 ? { pathHashSize } : {}),
|
||||
path,
|
||||
payload: {
|
||||
raw: payloadHex,
|
||||
decoded: decodedPayload
|
||||
},
|
||||
totalBytes: bytes.length,
|
||||
isValid: true
|
||||
};
|
||||
const structure = {
|
||||
segments,
|
||||
totalBytes: bytes.length,
|
||||
rawHex: hexData.toUpperCase(),
|
||||
messageHash,
|
||||
payload: {
|
||||
segments: payloadSegments,
|
||||
hex: payloadHex,
|
||||
startByte: offset,
|
||||
type: (0, enum_names_1.getPayloadTypeName)(payloadType)
|
||||
}
|
||||
};
|
||||
return { packet, structure };
|
||||
}
|
||||
catch (error) {
|
||||
const errorPacket = {
|
||||
messageHash: '',
|
||||
routeType: enums_1.RouteType.Flood,
|
||||
payloadType: enums_1.PayloadType.RawCustom,
|
||||
payloadVersion: enums_1.PayloadVersion.Version1,
|
||||
pathLength: 0,
|
||||
path: null,
|
||||
payload: { raw: '', decoded: null },
|
||||
totalBytes: bytes.length,
|
||||
isValid: false,
|
||||
errors: [error instanceof Error ? error.message : 'Unknown decoding error']
|
||||
};
|
||||
const errorStructure = {
|
||||
segments: [],
|
||||
totalBytes: bytes.length,
|
||||
rawHex: hexData.toUpperCase(),
|
||||
messageHash: '',
|
||||
payload: {
|
||||
segments: [],
|
||||
hex: '',
|
||||
startByte: 0,
|
||||
type: 'Unknown'
|
||||
}
|
||||
};
|
||||
return { packet: errorPacket, structure: errorStructure };
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Internal unified parsing method with signature verification for advertisements
|
||||
*/
|
||||
static async parseInternalAsync(hexData, includeStructure, options) {
|
||||
// First do the regular parsing
|
||||
const result = this.parseInternal(hexData, includeStructure, options);
|
||||
// If it's an advertisement, verify the signature
|
||||
if (result.packet.payloadType === enums_1.PayloadType.Advert && result.packet.payload.decoded) {
|
||||
try {
|
||||
const advertPayload = result.packet.payload.decoded;
|
||||
const verifiedAdvert = await advert_1.AdvertPayloadDecoder.decodeWithVerification((0, hex_1.hexToBytes)(result.packet.payload.raw), {
|
||||
includeSegments: includeStructure,
|
||||
segmentOffset: 0
|
||||
});
|
||||
if (verifiedAdvert) {
|
||||
// Update the payload with signature verification results
|
||||
result.packet.payload.decoded = verifiedAdvert;
|
||||
// If the advertisement signature is invalid, mark the whole packet as invalid
|
||||
if (!verifiedAdvert.isValid) {
|
||||
result.packet.isValid = false;
|
||||
result.packet.errors = verifiedAdvert.errors || ['Invalid advertisement signature'];
|
||||
}
|
||||
// Update structure segments if needed
|
||||
if (includeStructure && verifiedAdvert.segments) {
|
||||
result.structure.payload.segments = verifiedAdvert.segments;
|
||||
delete verifiedAdvert.segments;
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (error) {
|
||||
console.error('Signature verification failed:', error);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
/**
|
||||
* Validate packet format without full decoding
|
||||
*/
|
||||
static validate(hexData) {
|
||||
const bytes = (0, hex_1.hexToBytes)(hexData);
|
||||
const errors = [];
|
||||
if (bytes.length < 2) {
|
||||
errors.push('Packet too short (minimum 2 bytes required)');
|
||||
return { isValid: false, errors };
|
||||
}
|
||||
try {
|
||||
let offset = 1; // Skip header
|
||||
// check transport codes
|
||||
const header = bytes[0];
|
||||
const routeType = header & 0x03;
|
||||
if (routeType === enums_1.RouteType.TransportFlood || routeType === enums_1.RouteType.TransportDirect) {
|
||||
if (bytes.length < offset + 4) {
|
||||
errors.push('Packet too short for transport codes');
|
||||
}
|
||||
offset += 4;
|
||||
}
|
||||
// check path length
|
||||
if (bytes.length < offset + 1) {
|
||||
errors.push('Packet too short for path length');
|
||||
}
|
||||
else {
|
||||
const pathLenByte = bytes[offset];
|
||||
const { hashSize, byteLength } = this.decodePathLenByte(pathLenByte);
|
||||
offset += 1;
|
||||
if (hashSize === 4) {
|
||||
errors.push('Invalid path length byte: reserved hash size (bits 7:6 = 11)');
|
||||
}
|
||||
if (bytes.length < offset + byteLength) {
|
||||
errors.push('Packet too short for path data');
|
||||
}
|
||||
offset += byteLength;
|
||||
}
|
||||
// check if we have payload data
|
||||
if (offset >= bytes.length) {
|
||||
errors.push('No payload data found');
|
||||
}
|
||||
}
|
||||
catch (error) {
|
||||
errors.push(error instanceof Error ? error.message : 'Validation error');
|
||||
}
|
||||
return { isValid: errors.length === 0, errors: errors.length > 0 ? errors : undefined };
|
||||
}
|
||||
/**
|
||||
* Calculate message hash for a packet
|
||||
*/
|
||||
static calculateMessageHash(bytes, routeType, payloadType, payloadVersion) {
|
||||
// for TRACE packets, use the trace tag as hash
|
||||
if (payloadType === enums_1.PayloadType.Trace && bytes.length >= 13) {
|
||||
let offset = 1;
|
||||
// skip transport codes if present
|
||||
if (routeType === enums_1.RouteType.TransportFlood || routeType === enums_1.RouteType.TransportDirect) {
|
||||
offset += 4;
|
||||
}
|
||||
// skip path data (decode path_len byte for multi-byte hops)
|
||||
if (bytes.length > offset) {
|
||||
const { byteLength } = this.decodePathLenByte(bytes[offset]);
|
||||
offset += 1 + byteLength;
|
||||
}
|
||||
// extract trace tag
|
||||
if (bytes.length >= offset + 4) {
|
||||
const traceTag = (bytes[offset]) | (bytes[offset + 1] << 8) | (bytes[offset + 2] << 16) | (bytes[offset + 3] << 24);
|
||||
return (0, hex_1.numberToHex)(traceTag, 8);
|
||||
}
|
||||
}
|
||||
// for other packets, create hash from constant parts
|
||||
const constantHeader = (payloadType << 2) | (payloadVersion << 6);
|
||||
let offset = 1;
|
||||
// skip transport codes if present
|
||||
if (routeType === enums_1.RouteType.TransportFlood || routeType === enums_1.RouteType.TransportDirect) {
|
||||
offset += 4;
|
||||
}
|
||||
// skip path data (decode path_len byte for multi-byte hops)
|
||||
if (bytes.length > offset) {
|
||||
const { byteLength } = this.decodePathLenByte(bytes[offset]);
|
||||
offset += 1 + byteLength;
|
||||
}
|
||||
const payloadData = bytes.slice(offset);
|
||||
const hashInput = [constantHeader, ...Array.from(payloadData)];
|
||||
// generate hash
|
||||
let hash = 0;
|
||||
for (let i = 0; i < hashInput.length; i++) {
|
||||
hash = ((hash << 5) - hash + hashInput[i]) & 0xffffffff;
|
||||
}
|
||||
return (0, hex_1.numberToHex)(hash, 8);
|
||||
}
|
||||
/**
|
||||
* Create a key store for decryption
|
||||
*/
|
||||
static createKeyStore(initialKeys) {
|
||||
return new key_manager_1.MeshCoreKeyStore(initialKeys);
|
||||
}
|
||||
/**
|
||||
* Decode a path_len byte into hash size, hop count, and total byte length.
|
||||
* Firmware reference: Packet.h lines 79-83
|
||||
* Bits 7:6 = hash size selector: (path_len >> 6) + 1 = 1, 2, or 3 bytes per hop
|
||||
* Bits 5:0 = hop count (0-63)
|
||||
*/
|
||||
static decodePathLenByte(pathLenByte) {
|
||||
const hashSize = (pathLenByte >> 6) + 1;
|
||||
const hopCount = pathLenByte & 63;
|
||||
return { hashSize, hopCount, byteLength: hopCount * hashSize };
|
||||
}
|
||||
}
|
||||
exports.MeshCorePacketDecoder = MeshCorePacketDecoder;
|
||||
//# sourceMappingURL=packet-decoder.js.map
|
||||
1
frontend/lib/meshcore-decoder/dist/decoder/packet-decoder.js.map
vendored
Normal file
1
frontend/lib/meshcore-decoder/dist/decoder/packet-decoder.js.map
vendored
Normal file
File diff suppressed because one or more lines are too long
11
frontend/lib/meshcore-decoder/dist/decoder/payload-decoders/ack.d.ts
vendored
Normal file
11
frontend/lib/meshcore-decoder/dist/decoder/payload-decoders/ack.d.ts
vendored
Normal file
@@ -0,0 +1,11 @@
|
||||
import { AckPayload } from '../../types/payloads';
|
||||
import { PayloadSegment } from '../../types/packet';
|
||||
export declare class AckPayloadDecoder {
|
||||
static decode(payload: Uint8Array, options?: {
|
||||
includeSegments?: boolean;
|
||||
segmentOffset?: number;
|
||||
}): AckPayload & {
|
||||
segments?: PayloadSegment[];
|
||||
} | null;
|
||||
}
|
||||
//# sourceMappingURL=ack.d.ts.map
|
||||
1
frontend/lib/meshcore-decoder/dist/decoder/payload-decoders/ack.d.ts.map
vendored
Normal file
1
frontend/lib/meshcore-decoder/dist/decoder/payload-decoders/ack.d.ts.map
vendored
Normal file
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"ack.d.ts","sourceRoot":"","sources":["../../../src/decoder/payload-decoders/ack.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,UAAU,EAAE,MAAM,sBAAsB,CAAC;AAClD,OAAO,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AAIpD,qBAAa,iBAAiB;IAC5B,MAAM,CAAC,MAAM,CAAC,OAAO,EAAE,UAAU,EAAE,OAAO,CAAC,EAAE;QAAE,eAAe,CAAC,EAAE,OAAO,CAAC;QAAC,aAAa,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG,UAAU,GAAG;QAAE,QAAQ,CAAC,EAAE,cAAc,EAAE,CAAA;KAAE,GAAG,IAAI;CA2EzJ"}
|
||||
78
frontend/lib/meshcore-decoder/dist/decoder/payload-decoders/ack.js
vendored
Normal file
78
frontend/lib/meshcore-decoder/dist/decoder/payload-decoders/ack.js
vendored
Normal file
@@ -0,0 +1,78 @@
|
||||
"use strict";
|
||||
// Copyright (c) 2025 Michael Hart: https://github.com/michaelhart/meshcore-decoder
|
||||
// MIT License
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.AckPayloadDecoder = void 0;
|
||||
const enums_1 = require("../../types/enums");
|
||||
const hex_1 = require("../../utils/hex");
|
||||
class AckPayloadDecoder {
|
||||
static decode(payload, options) {
|
||||
try {
|
||||
// Based on MeshCore payloads.md - Ack payload structure:
|
||||
// - checksum (4 bytes) - CRC checksum of message timestamp, text, and sender pubkey
|
||||
if (payload.length < 4) {
|
||||
const result = {
|
||||
type: enums_1.PayloadType.Ack,
|
||||
version: enums_1.PayloadVersion.Version1,
|
||||
isValid: false,
|
||||
errors: ['Ack payload too short (minimum 4 bytes for checksum)'],
|
||||
checksum: ''
|
||||
};
|
||||
if (options?.includeSegments) {
|
||||
result.segments = [{
|
||||
name: 'Invalid Ack Data',
|
||||
description: 'Ack payload too short (minimum 4 bytes required for checksum)',
|
||||
startByte: options.segmentOffset || 0,
|
||||
endByte: (options.segmentOffset || 0) + payload.length - 1,
|
||||
value: (0, hex_1.bytesToHex)(payload)
|
||||
}];
|
||||
}
|
||||
return result;
|
||||
}
|
||||
const segments = [];
|
||||
const segmentOffset = options?.segmentOffset || 0;
|
||||
// parse checksum (4 bytes as hex)
|
||||
const checksum = (0, hex_1.bytesToHex)(payload.subarray(0, 4));
|
||||
if (options?.includeSegments) {
|
||||
segments.push({
|
||||
name: 'Checksum',
|
||||
description: `CRC checksum of message timestamp, text, and sender pubkey: 0x${checksum}`,
|
||||
startByte: segmentOffset,
|
||||
endByte: segmentOffset + 3,
|
||||
value: checksum
|
||||
});
|
||||
}
|
||||
// any additional data (if present)
|
||||
if (options?.includeSegments && payload.length > 4) {
|
||||
segments.push({
|
||||
name: 'Additional Data',
|
||||
description: 'Extra data in Ack payload',
|
||||
startByte: segmentOffset + 4,
|
||||
endByte: segmentOffset + payload.length - 1,
|
||||
value: (0, hex_1.bytesToHex)(payload.subarray(4))
|
||||
});
|
||||
}
|
||||
const result = {
|
||||
type: enums_1.PayloadType.Ack,
|
||||
version: enums_1.PayloadVersion.Version1,
|
||||
isValid: true,
|
||||
checksum
|
||||
};
|
||||
if (options?.includeSegments) {
|
||||
result.segments = segments;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
catch (error) {
|
||||
return {
|
||||
type: enums_1.PayloadType.Ack,
|
||||
version: enums_1.PayloadVersion.Version1,
|
||||
isValid: false,
|
||||
errors: [error instanceof Error ? error.message : 'Failed to decode Ack payload'],
|
||||
checksum: ''
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
exports.AckPayloadDecoder = AckPayloadDecoder;
|
||||
//# sourceMappingURL=ack.js.map
|
||||
1
frontend/lib/meshcore-decoder/dist/decoder/payload-decoders/ack.js.map
vendored
Normal file
1
frontend/lib/meshcore-decoder/dist/decoder/payload-decoders/ack.js.map
vendored
Normal file
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"ack.js","sourceRoot":"","sources":["../../../src/decoder/payload-decoders/ack.ts"],"names":[],"mappings":";AAAA,mFAAmF;AACnF,cAAc;;;AAId,6CAAgE;AAChE,yCAA6C;AAE7C,MAAa,iBAAiB;IAC5B,MAAM,CAAC,MAAM,CAAC,OAAmB,EAAE,OAA+D;QAChG,IAAI,CAAC;YACH,yDAAyD;YACzD,oFAAoF;YAEpF,IAAI,OAAO,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;gBACvB,MAAM,MAAM,GAAiD;oBAC3D,IAAI,EAAE,mBAAW,CAAC,GAAG;oBACrB,OAAO,EAAE,sBAAc,CAAC,QAAQ;oBAChC,OAAO,EAAE,KAAK;oBACd,MAAM,EAAE,CAAC,sDAAsD,CAAC;oBAChE,QAAQ,EAAE,EAAE;iBACb,CAAC;gBAEF,IAAI,OAAO,EAAE,eAAe,EAAE,CAAC;oBAC7B,MAAM,CAAC,QAAQ,GAAG,CAAC;4BACjB,IAAI,EAAE,kBAAkB;4BACxB,WAAW,EAAE,+DAA+D;4BAC5E,SAAS,EAAE,OAAO,CAAC,aAAa,IAAI,CAAC;4BACrC,OAAO,EAAE,CAAC,OAAO,CAAC,aAAa,IAAI,CAAC,CAAC,GAAG,OAAO,CAAC,MAAM,GAAG,CAAC;4BAC1D,KAAK,EAAE,IAAA,gBAAU,EAAC,OAAO,CAAC;yBAC3B,CAAC,CAAC;gBACL,CAAC;gBAED,OAAO,MAAM,CAAC;YAChB,CAAC;YAED,MAAM,QAAQ,GAAqB,EAAE,CAAC;YACtC,MAAM,aAAa,GAAG,OAAO,EAAE,aAAa,IAAI,CAAC,CAAC;YAElD,kCAAkC;YAClC,MAAM,QAAQ,GAAG,IAAA,gBAAU,EAAC,OAAO,CAAC,QAAQ,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;YACpD,IAAI,OAAO,EAAE,eAAe,EAAE,CAAC;gBAC7B,QAAQ,CAAC,IAAI,CAAC;oBACZ,IAAI,EAAE,UAAU;oBAChB,WAAW,EAAE,iEAAiE,QAAQ,EAAE;oBACxF,SAAS,EAAE,aAAa;oBACxB,OAAO,EAAE,aAAa,GAAG,CAAC;oBAC1B,KAAK,EAAE,QAAQ;iBAChB,CAAC,CAAC;YACL,CAAC;YAED,mCAAmC;YACnC,IAAI,OAAO,EAAE,eAAe,IAAI,OAAO,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;gBACnD,QAAQ,CAAC,IAAI,CAAC;oBACZ,IAAI,EAAE,iBAAiB;oBACvB,WAAW,EAAE,2BAA2B;oBACxC,SAAS,EAAE,aAAa,GAAG,CAAC;oBAC5B,OAAO,EAAE,aAAa,GAAG,OAAO,CAAC,MAAM,GAAG,CAAC;oBAC3C,KAAK,EAAE,IAAA,gBAAU,EAAC,OAAO,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC;iBACvC,CAAC,CAAC;YACL,CAAC;YAED,MAAM,MAAM,GAAiD;gBAC3D,IAAI,EAAE,mBAAW,CAAC,GAAG;gBACrB,OAAO,EAAE,sBAAc,CAAC,QAAQ;gBAChC,OAAO,EAAE,IAAI;gBACb,QAAQ;aACT,CAAC;YAEF,IAAI,OAAO,EAAE,eAAe,EAAE,CAAC;gBAC7B,MAAM,CAAC,QAAQ,GAAG,QAAQ,CAAC;YAC7B,CAAC;YAED,OAAO,MAAM,CAAC;QAChB,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,OAAO;gBACL,IAAI,EAAE,mBAAW,CAAC,GAAG;gBACrB,OAAO,EAAE,sBAAc,CAAC,QAAQ;gBAChC,OAAO,EAAE,KAAK;gBACd,MAAM,EAAE,CAAC,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,8BAA8B,CAAC;gBACjF,QAAQ,EAAE,EAAE;aACb,CAAC;QACJ,CAAC;IACH,CAAC;CACF;AA5ED,8CA4EC"}
|
||||
24
frontend/lib/meshcore-decoder/dist/decoder/payload-decoders/advert.d.ts
vendored
Normal file
24
frontend/lib/meshcore-decoder/dist/decoder/payload-decoders/advert.d.ts
vendored
Normal file
@@ -0,0 +1,24 @@
|
||||
import { AdvertPayload } from '../../types/payloads';
|
||||
import { PayloadSegment } from '../../types/packet';
|
||||
export declare class AdvertPayloadDecoder {
|
||||
static decode(payload: Uint8Array, options?: {
|
||||
includeSegments?: boolean;
|
||||
segmentOffset?: number;
|
||||
}): AdvertPayload & {
|
||||
segments?: PayloadSegment[];
|
||||
} | null;
|
||||
/**
|
||||
* Decode advertisement payload with signature verification
|
||||
*/
|
||||
static decodeWithVerification(payload: Uint8Array, options?: {
|
||||
includeSegments?: boolean;
|
||||
segmentOffset?: number;
|
||||
}): Promise<AdvertPayload & {
|
||||
segments?: PayloadSegment[];
|
||||
} | null>;
|
||||
private static parseDeviceRole;
|
||||
private static readUint32LE;
|
||||
private static readInt32LE;
|
||||
private static sanitizeControlCharacters;
|
||||
}
|
||||
//# sourceMappingURL=advert.d.ts.map
|
||||
1
frontend/lib/meshcore-decoder/dist/decoder/payload-decoders/advert.d.ts.map
vendored
Normal file
1
frontend/lib/meshcore-decoder/dist/decoder/payload-decoders/advert.d.ts.map
vendored
Normal file
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"advert.d.ts","sourceRoot":"","sources":["../../../src/decoder/payload-decoders/advert.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AACrD,OAAO,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AAMpD,qBAAa,oBAAoB;IAC/B,MAAM,CAAC,MAAM,CAAC,OAAO,EAAE,UAAU,EAAE,OAAO,CAAC,EAAE;QAAE,eAAe,CAAC,EAAE,OAAO,CAAC;QAAC,aAAa,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG,aAAa,GAAG;QAAE,QAAQ,CAAC,EAAE,cAAc,EAAE,CAAA;KAAE,GAAG,IAAI;IAuL3J;;OAEG;WACU,sBAAsB,CAAC,OAAO,EAAE,UAAU,EAAE,OAAO,CAAC,EAAE;QAAE,eAAe,CAAC,EAAE,OAAO,CAAC;QAAC,aAAa,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG,OAAO,CAAC,aAAa,GAAG;QAAE,QAAQ,CAAC,EAAE,cAAc,EAAE,CAAA;KAAE,GAAG,IAAI,CAAC;IA4C1L,OAAO,CAAC,MAAM,CAAC,eAAe;IAW9B,OAAO,CAAC,MAAM,CAAC,YAAY;IAO3B,OAAO,CAAC,MAAM,CAAC,WAAW;IAM1B,OAAO,CAAC,MAAM,CAAC,yBAAyB;CAKzC"}
|
||||
244
frontend/lib/meshcore-decoder/dist/decoder/payload-decoders/advert.js
vendored
Normal file
244
frontend/lib/meshcore-decoder/dist/decoder/payload-decoders/advert.js
vendored
Normal file
@@ -0,0 +1,244 @@
|
||||
"use strict";
|
||||
// Copyright (c) 2025 Michael Hart: https://github.com/michaelhart/meshcore-decoder
|
||||
// MIT License
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.AdvertPayloadDecoder = void 0;
|
||||
const enums_1 = require("../../types/enums");
|
||||
const hex_1 = require("../../utils/hex");
|
||||
const enum_names_1 = require("../../utils/enum-names");
|
||||
const ed25519_verifier_1 = require("../../crypto/ed25519-verifier");
|
||||
class AdvertPayloadDecoder {
|
||||
static decode(payload, options) {
|
||||
try {
|
||||
// start of appdata section: public_key(32) + timestamp(4) + signature(64) + flags(1) = 101 bytes
|
||||
if (payload.length < 101) {
|
||||
const result = {
|
||||
type: enums_1.PayloadType.Advert,
|
||||
version: enums_1.PayloadVersion.Version1,
|
||||
isValid: false,
|
||||
errors: ['Advertisement payload too short'],
|
||||
publicKey: '',
|
||||
timestamp: 0,
|
||||
signature: '',
|
||||
appData: {
|
||||
flags: 0,
|
||||
deviceRole: enums_1.DeviceRole.ChatNode,
|
||||
hasLocation: false,
|
||||
hasName: false
|
||||
}
|
||||
};
|
||||
if (options?.includeSegments) {
|
||||
result.segments = [{
|
||||
name: 'Invalid Advert Data',
|
||||
description: 'Advert payload too short (minimum 101 bytes required)',
|
||||
startByte: options.segmentOffset || 0,
|
||||
endByte: (options.segmentOffset || 0) + payload.length - 1,
|
||||
value: (0, hex_1.bytesToHex)(payload)
|
||||
}];
|
||||
}
|
||||
return result;
|
||||
}
|
||||
const segments = [];
|
||||
const segmentOffset = options?.segmentOffset || 0;
|
||||
let currentOffset = 0;
|
||||
// parse advertisement structure from payloads.md
|
||||
const publicKey = (0, hex_1.bytesToHex)(payload.subarray(currentOffset, currentOffset + 32));
|
||||
if (options?.includeSegments) {
|
||||
segments.push({
|
||||
name: 'Public Key',
|
||||
description: 'Ed25519 public key',
|
||||
startByte: segmentOffset + currentOffset,
|
||||
endByte: segmentOffset + currentOffset + 31,
|
||||
value: publicKey
|
||||
});
|
||||
}
|
||||
currentOffset += 32;
|
||||
const timestamp = this.readUint32LE(payload, currentOffset);
|
||||
if (options?.includeSegments) {
|
||||
const timestampDate = new Date(timestamp * 1000);
|
||||
segments.push({
|
||||
name: 'Timestamp',
|
||||
description: `${timestamp} (${timestampDate.toISOString().slice(0, 19)}Z)`,
|
||||
startByte: segmentOffset + currentOffset,
|
||||
endByte: segmentOffset + currentOffset + 3,
|
||||
value: (0, hex_1.bytesToHex)(payload.subarray(currentOffset, currentOffset + 4))
|
||||
});
|
||||
}
|
||||
currentOffset += 4;
|
||||
const signature = (0, hex_1.bytesToHex)(payload.subarray(currentOffset, currentOffset + 64));
|
||||
if (options?.includeSegments) {
|
||||
segments.push({
|
||||
name: 'Signature',
|
||||
description: 'Ed25519 signature',
|
||||
startByte: segmentOffset + currentOffset,
|
||||
endByte: segmentOffset + currentOffset + 63,
|
||||
value: signature
|
||||
});
|
||||
}
|
||||
currentOffset += 64;
|
||||
const flags = payload[currentOffset];
|
||||
if (options?.includeSegments) {
|
||||
const binaryStr = flags.toString(2).padStart(8, '0');
|
||||
const deviceRole = this.parseDeviceRole(flags);
|
||||
const roleName = (0, enum_names_1.getDeviceRoleName)(deviceRole);
|
||||
const flagDesc = ` | Bits 0-3 (Role): ${roleName} | Bit 4 (Location): ${!!(flags & enums_1.AdvertFlags.HasLocation) ? 'Yes' : 'No'} | Bit 7 (Name): ${!!(flags & enums_1.AdvertFlags.HasName) ? 'Yes' : 'No'}`;
|
||||
segments.push({
|
||||
name: 'App Flags',
|
||||
description: `Binary: ${binaryStr}${flagDesc}`,
|
||||
startByte: segmentOffset + currentOffset,
|
||||
endByte: segmentOffset + currentOffset,
|
||||
value: flags.toString(16).padStart(2, '0').toUpperCase()
|
||||
});
|
||||
}
|
||||
currentOffset += 1;
|
||||
const advert = {
|
||||
type: enums_1.PayloadType.Advert,
|
||||
version: enums_1.PayloadVersion.Version1,
|
||||
isValid: true,
|
||||
publicKey,
|
||||
timestamp,
|
||||
signature,
|
||||
appData: {
|
||||
flags,
|
||||
deviceRole: this.parseDeviceRole(flags),
|
||||
hasLocation: !!(flags & enums_1.AdvertFlags.HasLocation),
|
||||
hasName: !!(flags & enums_1.AdvertFlags.HasName)
|
||||
}
|
||||
};
|
||||
let offset = currentOffset;
|
||||
// location data (if HasLocation flag is set)
|
||||
if (flags & enums_1.AdvertFlags.HasLocation && payload.length >= offset + 8) {
|
||||
const lat = this.readInt32LE(payload, offset) / 1000000;
|
||||
const lon = this.readInt32LE(payload, offset + 4) / 1000000;
|
||||
advert.appData.location = {
|
||||
latitude: Math.round(lat * 1000000) / 1000000, // Keep precision
|
||||
longitude: Math.round(lon * 1000000) / 1000000
|
||||
};
|
||||
if (options?.includeSegments) {
|
||||
segments.push({
|
||||
name: 'Latitude',
|
||||
description: `${lat}° (${lat})`,
|
||||
startByte: segmentOffset + offset,
|
||||
endByte: segmentOffset + offset + 3,
|
||||
value: (0, hex_1.bytesToHex)(payload.subarray(offset, offset + 4))
|
||||
});
|
||||
segments.push({
|
||||
name: 'Longitude',
|
||||
description: `${lon}° (${lon})`,
|
||||
startByte: segmentOffset + offset + 4,
|
||||
endByte: segmentOffset + offset + 7,
|
||||
value: (0, hex_1.bytesToHex)(payload.subarray(offset + 4, offset + 8))
|
||||
});
|
||||
}
|
||||
offset += 8;
|
||||
}
|
||||
// skip feature fields for now (HasFeature1, HasFeature2)
|
||||
if (flags & enums_1.AdvertFlags.HasFeature1)
|
||||
offset += 2;
|
||||
if (flags & enums_1.AdvertFlags.HasFeature2)
|
||||
offset += 2;
|
||||
// name data (if HasName flag is set)
|
||||
if (flags & enums_1.AdvertFlags.HasName && payload.length > offset) {
|
||||
const nameBytes = payload.subarray(offset);
|
||||
const rawName = new TextDecoder('utf-8').decode(nameBytes).replace(/\0.*$/, '');
|
||||
advert.appData.name = this.sanitizeControlCharacters(rawName) || rawName;
|
||||
if (options?.includeSegments) {
|
||||
segments.push({
|
||||
name: 'Node Name',
|
||||
description: `Node name: "${advert.appData.name}"`,
|
||||
startByte: segmentOffset + offset,
|
||||
endByte: segmentOffset + payload.length - 1,
|
||||
value: (0, hex_1.bytesToHex)(nameBytes)
|
||||
});
|
||||
}
|
||||
}
|
||||
if (options?.includeSegments) {
|
||||
advert.segments = segments;
|
||||
}
|
||||
return advert;
|
||||
}
|
||||
catch (error) {
|
||||
return {
|
||||
type: enums_1.PayloadType.Advert,
|
||||
version: enums_1.PayloadVersion.Version1,
|
||||
isValid: false,
|
||||
errors: [error instanceof Error ? error.message : 'Failed to decode advertisement payload'],
|
||||
publicKey: '',
|
||||
timestamp: 0,
|
||||
signature: '',
|
||||
appData: {
|
||||
flags: 0,
|
||||
deviceRole: enums_1.DeviceRole.ChatNode,
|
||||
hasLocation: false,
|
||||
hasName: false
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Decode advertisement payload with signature verification
|
||||
*/
|
||||
static async decodeWithVerification(payload, options) {
|
||||
// First decode normally
|
||||
const advert = this.decode(payload, options);
|
||||
if (!advert || !advert.isValid) {
|
||||
return advert;
|
||||
}
|
||||
// Perform signature verification
|
||||
try {
|
||||
// Extract app_data from the payload (everything after public_key + timestamp + signature)
|
||||
const appDataStart = 32 + 4 + 64; // public_key + timestamp + signature
|
||||
const appDataBytes = payload.subarray(appDataStart);
|
||||
const appDataHex = (0, hex_1.bytesToHex)(appDataBytes);
|
||||
const signatureValid = await ed25519_verifier_1.Ed25519SignatureVerifier.verifyAdvertisementSignature(advert.publicKey, advert.signature, advert.timestamp, appDataHex);
|
||||
advert.signatureValid = signatureValid;
|
||||
if (!signatureValid) {
|
||||
advert.signatureError = 'Ed25519 signature verification failed';
|
||||
advert.isValid = false;
|
||||
if (!advert.errors) {
|
||||
advert.errors = [];
|
||||
}
|
||||
advert.errors.push('Invalid Ed25519 signature');
|
||||
}
|
||||
}
|
||||
catch (error) {
|
||||
advert.signatureValid = false;
|
||||
advert.signatureError = error instanceof Error ? error.message : 'Signature verification error';
|
||||
advert.isValid = false;
|
||||
if (!advert.errors) {
|
||||
advert.errors = [];
|
||||
}
|
||||
advert.errors.push('Signature verification failed: ' + (error instanceof Error ? error.message : 'Unknown error'));
|
||||
}
|
||||
return advert;
|
||||
}
|
||||
static parseDeviceRole(flags) {
|
||||
const roleValue = flags & 0x0F;
|
||||
switch (roleValue) {
|
||||
case 0x01: return enums_1.DeviceRole.ChatNode;
|
||||
case 0x02: return enums_1.DeviceRole.Repeater;
|
||||
case 0x03: return enums_1.DeviceRole.RoomServer;
|
||||
case 0x04: return enums_1.DeviceRole.Sensor;
|
||||
default: return enums_1.DeviceRole.ChatNode;
|
||||
}
|
||||
}
|
||||
static readUint32LE(buffer, offset) {
|
||||
return buffer[offset] |
|
||||
(buffer[offset + 1] << 8) |
|
||||
(buffer[offset + 2] << 16) |
|
||||
(buffer[offset + 3] << 24);
|
||||
}
|
||||
static readInt32LE(buffer, offset) {
|
||||
const value = this.readUint32LE(buffer, offset);
|
||||
// convert unsigned to signed
|
||||
return value > 0x7FFFFFFF ? value - 0x100000000 : value;
|
||||
}
|
||||
static sanitizeControlCharacters(value) {
|
||||
if (!value)
|
||||
return null;
|
||||
const sanitized = value.trim().replace(/[\x00-\x1F\x7F]/g, '');
|
||||
return sanitized || null;
|
||||
}
|
||||
}
|
||||
exports.AdvertPayloadDecoder = AdvertPayloadDecoder;
|
||||
//# sourceMappingURL=advert.js.map
|
||||
1
frontend/lib/meshcore-decoder/dist/decoder/payload-decoders/advert.js.map
vendored
Normal file
1
frontend/lib/meshcore-decoder/dist/decoder/payload-decoders/advert.js.map
vendored
Normal file
File diff suppressed because one or more lines are too long
11
frontend/lib/meshcore-decoder/dist/decoder/payload-decoders/anon-request.d.ts
vendored
Normal file
11
frontend/lib/meshcore-decoder/dist/decoder/payload-decoders/anon-request.d.ts
vendored
Normal file
@@ -0,0 +1,11 @@
|
||||
import { AnonRequestPayload } from '../../types/payloads';
|
||||
import { PayloadSegment } from '../../types/packet';
|
||||
export declare class AnonRequestPayloadDecoder {
|
||||
static decode(payload: Uint8Array, options?: {
|
||||
includeSegments?: boolean;
|
||||
segmentOffset?: number;
|
||||
}): AnonRequestPayload & {
|
||||
segments?: PayloadSegment[];
|
||||
} | null;
|
||||
}
|
||||
//# sourceMappingURL=anon-request.d.ts.map
|
||||
1
frontend/lib/meshcore-decoder/dist/decoder/payload-decoders/anon-request.d.ts.map
vendored
Normal file
1
frontend/lib/meshcore-decoder/dist/decoder/payload-decoders/anon-request.d.ts.map
vendored
Normal file
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"anon-request.d.ts","sourceRoot":"","sources":["../../../src/decoder/payload-decoders/anon-request.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,kBAAkB,EAAE,MAAM,sBAAsB,CAAC;AAC1D,OAAO,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AAIpD,qBAAa,yBAAyB;IACpC,MAAM,CAAC,MAAM,CAAC,OAAO,EAAE,UAAU,EAAE,OAAO,CAAC,EAAE;QAAE,eAAe,CAAC,EAAE,OAAO,CAAC;QAAC,aAAa,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG,kBAAkB,GAAG;QAAE,QAAQ,CAAC,EAAE,cAAc,EAAE,CAAA;KAAE,GAAG,IAAI;CA8HjK"}
|
||||
123
frontend/lib/meshcore-decoder/dist/decoder/payload-decoders/anon-request.js
vendored
Normal file
123
frontend/lib/meshcore-decoder/dist/decoder/payload-decoders/anon-request.js
vendored
Normal file
@@ -0,0 +1,123 @@
|
||||
"use strict";
|
||||
// Copyright (c) 2025 Michael Hart: https://github.com/michaelhart/meshcore-decoder
|
||||
// MIT License
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.AnonRequestPayloadDecoder = void 0;
|
||||
const enums_1 = require("../../types/enums");
|
||||
const hex_1 = require("../../utils/hex");
|
||||
class AnonRequestPayloadDecoder {
|
||||
static decode(payload, options) {
|
||||
try {
|
||||
// Based on MeshCore payloads.md - AnonRequest payload structure:
|
||||
// - destination_hash (1 byte)
|
||||
// - sender_public_key (32 bytes)
|
||||
// - cipher_mac (2 bytes)
|
||||
// - ciphertext (rest of payload)
|
||||
if (payload.length < 35) {
|
||||
const result = {
|
||||
type: enums_1.PayloadType.AnonRequest,
|
||||
version: enums_1.PayloadVersion.Version1,
|
||||
isValid: false,
|
||||
errors: ['AnonRequest payload too short (minimum 35 bytes: dest + public key + MAC)'],
|
||||
destinationHash: '',
|
||||
senderPublicKey: '',
|
||||
cipherMac: '',
|
||||
ciphertext: '',
|
||||
ciphertextLength: 0
|
||||
};
|
||||
if (options?.includeSegments) {
|
||||
result.segments = [{
|
||||
name: 'Invalid AnonRequest Data',
|
||||
description: 'AnonRequest payload too short (minimum 35 bytes required: 1 for dest hash + 32 for public key + 2 for MAC)',
|
||||
startByte: options.segmentOffset || 0,
|
||||
endByte: (options.segmentOffset || 0) + payload.length - 1,
|
||||
value: (0, hex_1.bytesToHex)(payload)
|
||||
}];
|
||||
}
|
||||
return result;
|
||||
}
|
||||
const segments = [];
|
||||
const segmentOffset = options?.segmentOffset || 0;
|
||||
let offset = 0;
|
||||
// Parse destination hash (1 byte)
|
||||
const destinationHash = (0, hex_1.byteToHex)(payload[0]);
|
||||
if (options?.includeSegments) {
|
||||
segments.push({
|
||||
name: 'Destination Hash',
|
||||
description: `First byte of destination node public key: 0x${destinationHash}`,
|
||||
startByte: segmentOffset + offset,
|
||||
endByte: segmentOffset + offset,
|
||||
value: destinationHash
|
||||
});
|
||||
}
|
||||
offset += 1;
|
||||
// Parse sender public key (32 bytes)
|
||||
const senderPublicKey = (0, hex_1.bytesToHex)(payload.subarray(1, 33));
|
||||
if (options?.includeSegments) {
|
||||
segments.push({
|
||||
name: 'Sender Public Key',
|
||||
description: `Ed25519 public key of the sender (32 bytes)`,
|
||||
startByte: segmentOffset + offset,
|
||||
endByte: segmentOffset + offset + 31,
|
||||
value: senderPublicKey
|
||||
});
|
||||
}
|
||||
offset += 32;
|
||||
// Parse cipher MAC (2 bytes)
|
||||
const cipherMac = (0, hex_1.bytesToHex)(payload.subarray(33, 35));
|
||||
if (options?.includeSegments) {
|
||||
segments.push({
|
||||
name: 'Cipher MAC',
|
||||
description: `MAC for encrypted data verification (2 bytes)`,
|
||||
startByte: segmentOffset + offset,
|
||||
endByte: segmentOffset + offset + 1,
|
||||
value: cipherMac
|
||||
});
|
||||
}
|
||||
offset += 2;
|
||||
// Parse ciphertext (remaining bytes)
|
||||
const ciphertext = (0, hex_1.bytesToHex)(payload.subarray(35));
|
||||
if (options?.includeSegments && payload.length > 35) {
|
||||
segments.push({
|
||||
name: 'Ciphertext',
|
||||
description: `Encrypted message data (${payload.length - 35} bytes). Contains encrypted plaintext with this structure:
|
||||
• Timestamp (4 bytes) - send time as unix timestamp
|
||||
• Sync Timestamp (4 bytes) - room server only, sender's "sync messages SINCE x" timestamp
|
||||
• Password (remaining bytes) - password for repeater/room`,
|
||||
startByte: segmentOffset + offset,
|
||||
endByte: segmentOffset + payload.length - 1,
|
||||
value: ciphertext
|
||||
});
|
||||
}
|
||||
const result = {
|
||||
type: enums_1.PayloadType.AnonRequest,
|
||||
version: enums_1.PayloadVersion.Version1,
|
||||
isValid: true,
|
||||
destinationHash,
|
||||
senderPublicKey,
|
||||
cipherMac,
|
||||
ciphertext,
|
||||
ciphertextLength: payload.length - 35
|
||||
};
|
||||
if (options?.includeSegments) {
|
||||
result.segments = segments;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
catch (error) {
|
||||
return {
|
||||
type: enums_1.PayloadType.AnonRequest,
|
||||
version: enums_1.PayloadVersion.Version1,
|
||||
isValid: false,
|
||||
errors: [error instanceof Error ? error.message : 'Failed to decode AnonRequest payload'],
|
||||
destinationHash: '',
|
||||
senderPublicKey: '',
|
||||
cipherMac: '',
|
||||
ciphertext: '',
|
||||
ciphertextLength: 0
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
exports.AnonRequestPayloadDecoder = AnonRequestPayloadDecoder;
|
||||
//# sourceMappingURL=anon-request.js.map
|
||||
1
frontend/lib/meshcore-decoder/dist/decoder/payload-decoders/anon-request.js.map
vendored
Normal file
1
frontend/lib/meshcore-decoder/dist/decoder/payload-decoders/anon-request.js.map
vendored
Normal file
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"anon-request.js","sourceRoot":"","sources":["../../../src/decoder/payload-decoders/anon-request.ts"],"names":[],"mappings":"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"}
|
||||
16
frontend/lib/meshcore-decoder/dist/decoder/payload-decoders/control.d.ts
vendored
Normal file
16
frontend/lib/meshcore-decoder/dist/decoder/payload-decoders/control.d.ts
vendored
Normal file
@@ -0,0 +1,16 @@
|
||||
import { ControlPayload } from '../../types/payloads';
|
||||
import { PayloadSegment } from '../../types/packet';
|
||||
export declare class ControlPayloadDecoder {
|
||||
static decode(payload: Uint8Array, options?: {
|
||||
includeSegments?: boolean;
|
||||
segmentOffset?: number;
|
||||
}): (ControlPayload & {
|
||||
segments?: PayloadSegment[];
|
||||
}) | null;
|
||||
private static decodeDiscoverReq;
|
||||
private static decodeDiscoverResp;
|
||||
private static parseTypeFilter;
|
||||
private static createErrorPayload;
|
||||
private static readUint32LE;
|
||||
}
|
||||
//# sourceMappingURL=control.d.ts.map
|
||||
1
frontend/lib/meshcore-decoder/dist/decoder/payload-decoders/control.d.ts.map
vendored
Normal file
1
frontend/lib/meshcore-decoder/dist/decoder/payload-decoders/control.d.ts.map
vendored
Normal file
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"control.d.ts","sourceRoot":"","sources":["../../../src/decoder/payload-decoders/control.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,cAAc,EAAyD,MAAM,sBAAsB,CAAC;AAC7G,OAAO,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AAKpD,qBAAa,qBAAqB;IAChC,MAAM,CAAC,MAAM,CAAC,OAAO,EAAE,UAAU,EAAE,OAAO,CAAC,EAAE;QAAE,eAAe,CAAC,EAAE,OAAO,CAAC;QAAC,aAAa,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG,CAAC,cAAc,GAAG;QAAE,QAAQ,CAAC,EAAE,cAAc,EAAE,CAAA;KAAE,CAAC,GAAG,IAAI;IAsB9J,OAAO,CAAC,MAAM,CAAC,iBAAiB;IAoHhC,OAAO,CAAC,MAAM,CAAC,kBAAkB;IAwHjC,OAAO,CAAC,MAAM,CAAC,eAAe;IAS9B,OAAO,CAAC,MAAM,CAAC,kBAAkB;IAgCjC,OAAO,CAAC,MAAM,CAAC,YAAY;CAM5B"}
|
||||
279
frontend/lib/meshcore-decoder/dist/decoder/payload-decoders/control.js
vendored
Normal file
279
frontend/lib/meshcore-decoder/dist/decoder/payload-decoders/control.js
vendored
Normal file
@@ -0,0 +1,279 @@
|
||||
"use strict";
|
||||
// Copyright (c) 2025 Michael Hart: https://github.com/michaelhart/meshcore-decoder
|
||||
// MIT License
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.ControlPayloadDecoder = void 0;
|
||||
const enums_1 = require("../../types/enums");
|
||||
const hex_1 = require("../../utils/hex");
|
||||
const enum_names_1 = require("../../utils/enum-names");
|
||||
class ControlPayloadDecoder {
|
||||
static decode(payload, options) {
|
||||
try {
|
||||
if (payload.length < 1) {
|
||||
return this.createErrorPayload('Control payload too short (minimum 1 byte required)', payload, options);
|
||||
}
|
||||
const rawFlags = payload[0];
|
||||
const subType = rawFlags & 0xF0; // upper 4 bits
|
||||
switch (subType) {
|
||||
case enums_1.ControlSubType.NodeDiscoverReq:
|
||||
return this.decodeDiscoverReq(payload, options);
|
||||
case enums_1.ControlSubType.NodeDiscoverResp:
|
||||
return this.decodeDiscoverResp(payload, options);
|
||||
default:
|
||||
return this.createErrorPayload(`Unknown control sub-type: 0x${subType.toString(16).padStart(2, '0')}`, payload, options);
|
||||
}
|
||||
}
|
||||
catch (error) {
|
||||
return this.createErrorPayload(error instanceof Error ? error.message : 'Failed to decode control payload', payload, options);
|
||||
}
|
||||
}
|
||||
static decodeDiscoverReq(payload, options) {
|
||||
const segments = [];
|
||||
const segmentOffset = options?.segmentOffset ?? 0;
|
||||
// Minimum size: flags(1) + type_filter(1) + tag(4) = 6 bytes
|
||||
if (payload.length < 6) {
|
||||
const result = {
|
||||
type: enums_1.PayloadType.Control,
|
||||
version: enums_1.PayloadVersion.Version1,
|
||||
isValid: false,
|
||||
errors: ['DISCOVER_REQ payload too short (minimum 6 bytes required)'],
|
||||
subType: enums_1.ControlSubType.NodeDiscoverReq,
|
||||
rawFlags: payload[0],
|
||||
prefixOnly: false,
|
||||
typeFilter: 0,
|
||||
typeFilterNames: [],
|
||||
tag: 0,
|
||||
since: 0
|
||||
};
|
||||
if (options?.includeSegments) {
|
||||
result.segments = [{
|
||||
name: 'Invalid DISCOVER_REQ Data',
|
||||
description: 'DISCOVER_REQ payload too short (minimum 6 bytes required)',
|
||||
startByte: segmentOffset,
|
||||
endByte: segmentOffset + payload.length - 1,
|
||||
value: (0, hex_1.bytesToHex)(payload)
|
||||
}];
|
||||
}
|
||||
return result;
|
||||
}
|
||||
let offset = 0;
|
||||
// Byte 0: flags - upper 4 bits is sub_type (0x8), lowest bit is prefix_only
|
||||
const rawFlags = payload[offset];
|
||||
const prefixOnly = (rawFlags & 0x01) !== 0;
|
||||
if (options?.includeSegments) {
|
||||
segments.push({
|
||||
name: 'Flags',
|
||||
description: `Sub-type: DISCOVER_REQ (0x8) | Prefix Only: ${prefixOnly}`,
|
||||
startByte: segmentOffset + offset,
|
||||
endByte: segmentOffset + offset,
|
||||
value: rawFlags.toString(16).padStart(2, '0').toUpperCase()
|
||||
});
|
||||
}
|
||||
offset += 1;
|
||||
// Byte 1: type_filter - bit for each ADV_TYPE_*
|
||||
const typeFilter = payload[offset];
|
||||
const typeFilterNames = this.parseTypeFilter(typeFilter);
|
||||
if (options?.includeSegments) {
|
||||
segments.push({
|
||||
name: 'Type Filter',
|
||||
description: `Filter mask: 0b${typeFilter.toString(2).padStart(8, '0')} | Types: ${typeFilterNames.length > 0 ? typeFilterNames.join(', ') : 'None'}`,
|
||||
startByte: segmentOffset + offset,
|
||||
endByte: segmentOffset + offset,
|
||||
value: typeFilter.toString(16).padStart(2, '0').toUpperCase()
|
||||
});
|
||||
}
|
||||
offset += 1;
|
||||
// Bytes 2-5: tag (uint32, little endian)
|
||||
const tag = this.readUint32LE(payload, offset);
|
||||
if (options?.includeSegments) {
|
||||
segments.push({
|
||||
name: 'Tag',
|
||||
description: `Random tag for response matching: 0x${tag.toString(16).padStart(8, '0')}`,
|
||||
startByte: segmentOffset + offset,
|
||||
endByte: segmentOffset + offset + 3,
|
||||
value: (0, hex_1.bytesToHex)(payload.slice(offset, offset + 4))
|
||||
});
|
||||
}
|
||||
offset += 4;
|
||||
// Optional: Bytes 6-9: since (uint32, little endian) - epoch timestamp
|
||||
let since = 0;
|
||||
if (payload.length >= offset + 4) {
|
||||
since = this.readUint32LE(payload, offset);
|
||||
if (options?.includeSegments) {
|
||||
const sinceDate = since > 0 ? new Date(since * 1000).toISOString().slice(0, 19) + 'Z' : 'N/A';
|
||||
segments.push({
|
||||
name: 'Since',
|
||||
description: `Filter timestamp: ${since} (${sinceDate})`,
|
||||
startByte: segmentOffset + offset,
|
||||
endByte: segmentOffset + offset + 3,
|
||||
value: (0, hex_1.bytesToHex)(payload.slice(offset, offset + 4))
|
||||
});
|
||||
}
|
||||
}
|
||||
const result = {
|
||||
type: enums_1.PayloadType.Control,
|
||||
version: enums_1.PayloadVersion.Version1,
|
||||
isValid: true,
|
||||
subType: enums_1.ControlSubType.NodeDiscoverReq,
|
||||
rawFlags,
|
||||
prefixOnly,
|
||||
typeFilter,
|
||||
typeFilterNames,
|
||||
tag,
|
||||
since
|
||||
};
|
||||
if (options?.includeSegments) {
|
||||
result.segments = segments;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
static decodeDiscoverResp(payload, options) {
|
||||
const segments = [];
|
||||
const segmentOffset = options?.segmentOffset ?? 0;
|
||||
// Minimum size: flags(1) + snr(1) + tag(4) + pubkey(8 for prefix) = 14 bytes
|
||||
if (payload.length < 14) {
|
||||
const result = {
|
||||
type: enums_1.PayloadType.Control,
|
||||
version: enums_1.PayloadVersion.Version1,
|
||||
isValid: false,
|
||||
errors: ['DISCOVER_RESP payload too short (minimum 14 bytes required)'],
|
||||
subType: enums_1.ControlSubType.NodeDiscoverResp,
|
||||
rawFlags: payload.length > 0 ? payload[0] : 0,
|
||||
nodeType: enums_1.DeviceRole.Unknown,
|
||||
nodeTypeName: 'Unknown',
|
||||
snr: 0,
|
||||
tag: 0,
|
||||
publicKey: '',
|
||||
publicKeyLength: 0
|
||||
};
|
||||
if (options?.includeSegments) {
|
||||
result.segments = [{
|
||||
name: 'Invalid DISCOVER_RESP Data',
|
||||
description: 'DISCOVER_RESP payload too short (minimum 14 bytes required)',
|
||||
startByte: segmentOffset,
|
||||
endByte: segmentOffset + payload.length - 1,
|
||||
value: (0, hex_1.bytesToHex)(payload)
|
||||
}];
|
||||
}
|
||||
return result;
|
||||
}
|
||||
let offset = 0;
|
||||
// Byte 0: flags - upper 4 bits is sub_type (0x9), lower 4 bits is node_type
|
||||
const rawFlags = payload[offset];
|
||||
const nodeType = (rawFlags & 0x0F);
|
||||
const nodeTypeName = (0, enum_names_1.getDeviceRoleName)(nodeType);
|
||||
if (options?.includeSegments) {
|
||||
segments.push({
|
||||
name: 'Flags',
|
||||
description: `Sub-type: DISCOVER_RESP (0x9) | Node Type: ${nodeTypeName}`,
|
||||
startByte: segmentOffset + offset,
|
||||
endByte: segmentOffset + offset,
|
||||
value: rawFlags.toString(16).padStart(2, '0').toUpperCase()
|
||||
});
|
||||
}
|
||||
offset += 1;
|
||||
// Byte 1: snr (signed int8, represents SNR * 4)
|
||||
const snrRaw = payload[offset];
|
||||
const snrSigned = snrRaw > 127 ? snrRaw - 256 : snrRaw;
|
||||
const snr = snrSigned / 4.0;
|
||||
if (options?.includeSegments) {
|
||||
segments.push({
|
||||
name: 'SNR',
|
||||
description: `Inbound SNR: ${snr.toFixed(2)} dB (raw: ${snrRaw}, signed: ${snrSigned})`,
|
||||
startByte: segmentOffset + offset,
|
||||
endByte: segmentOffset + offset,
|
||||
value: snrRaw.toString(16).padStart(2, '0').toUpperCase()
|
||||
});
|
||||
}
|
||||
offset += 1;
|
||||
// Bytes 2-5: tag (uint32, little endian) - reflected from request
|
||||
const tag = this.readUint32LE(payload, offset);
|
||||
if (options?.includeSegments) {
|
||||
segments.push({
|
||||
name: 'Tag',
|
||||
description: `Reflected tag from request: 0x${tag.toString(16).padStart(8, '0')}`,
|
||||
startByte: segmentOffset + offset,
|
||||
endByte: segmentOffset + offset + 3,
|
||||
value: (0, hex_1.bytesToHex)(payload.slice(offset, offset + 4))
|
||||
});
|
||||
}
|
||||
offset += 4;
|
||||
// Remaining bytes: public key (8 bytes for prefix, 32 bytes for full)
|
||||
const remainingBytes = payload.length - offset;
|
||||
const publicKeyLength = remainingBytes;
|
||||
const publicKeyBytes = payload.slice(offset, offset + publicKeyLength);
|
||||
const publicKey = (0, hex_1.bytesToHex)(publicKeyBytes);
|
||||
if (options?.includeSegments) {
|
||||
const keyType = publicKeyLength === 32 ? 'Full Public Key' : 'Public Key Prefix';
|
||||
segments.push({
|
||||
name: keyType,
|
||||
description: `${keyType} (${publicKeyLength} bytes)`,
|
||||
startByte: segmentOffset + offset,
|
||||
endByte: segmentOffset + offset + publicKeyLength - 1,
|
||||
value: publicKey
|
||||
});
|
||||
}
|
||||
const result = {
|
||||
type: enums_1.PayloadType.Control,
|
||||
version: enums_1.PayloadVersion.Version1,
|
||||
isValid: true,
|
||||
subType: enums_1.ControlSubType.NodeDiscoverResp,
|
||||
rawFlags,
|
||||
nodeType,
|
||||
nodeTypeName,
|
||||
snr,
|
||||
tag,
|
||||
publicKey,
|
||||
publicKeyLength
|
||||
};
|
||||
if (options?.includeSegments) {
|
||||
result.segments = segments;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
static parseTypeFilter(filter) {
|
||||
const types = [];
|
||||
if (filter & (1 << enums_1.DeviceRole.ChatNode))
|
||||
types.push('Chat');
|
||||
if (filter & (1 << enums_1.DeviceRole.Repeater))
|
||||
types.push('Repeater');
|
||||
if (filter & (1 << enums_1.DeviceRole.RoomServer))
|
||||
types.push('Room');
|
||||
if (filter & (1 << enums_1.DeviceRole.Sensor))
|
||||
types.push('Sensor');
|
||||
return types;
|
||||
}
|
||||
static createErrorPayload(error, payload, options) {
|
||||
const result = {
|
||||
type: enums_1.PayloadType.Control,
|
||||
version: enums_1.PayloadVersion.Version1,
|
||||
isValid: false,
|
||||
errors: [error],
|
||||
subType: enums_1.ControlSubType.NodeDiscoverReq,
|
||||
rawFlags: payload.length > 0 ? payload[0] : 0,
|
||||
prefixOnly: false,
|
||||
typeFilter: 0,
|
||||
typeFilterNames: [],
|
||||
tag: 0,
|
||||
since: 0
|
||||
};
|
||||
if (options?.includeSegments) {
|
||||
result.segments = [{
|
||||
name: 'Invalid Control Data',
|
||||
description: error,
|
||||
startByte: options.segmentOffset ?? 0,
|
||||
endByte: (options.segmentOffset ?? 0) + payload.length - 1,
|
||||
value: (0, hex_1.bytesToHex)(payload)
|
||||
}];
|
||||
}
|
||||
return result;
|
||||
}
|
||||
static readUint32LE(buffer, offset) {
|
||||
return (buffer[offset] |
|
||||
(buffer[offset + 1] << 8) |
|
||||
(buffer[offset + 2] << 16) |
|
||||
(buffer[offset + 3] << 24)) >>> 0; // >>> 0 to ensure unsigned
|
||||
}
|
||||
}
|
||||
exports.ControlPayloadDecoder = ControlPayloadDecoder;
|
||||
//# sourceMappingURL=control.js.map
|
||||
1
frontend/lib/meshcore-decoder/dist/decoder/payload-decoders/control.js.map
vendored
Normal file
1
frontend/lib/meshcore-decoder/dist/decoder/payload-decoders/control.js.map
vendored
Normal file
File diff suppressed because one or more lines are too long
12
frontend/lib/meshcore-decoder/dist/decoder/payload-decoders/group-text.d.ts
vendored
Normal file
12
frontend/lib/meshcore-decoder/dist/decoder/payload-decoders/group-text.d.ts
vendored
Normal file
@@ -0,0 +1,12 @@
|
||||
import { GroupTextPayload } from '../../types/payloads';
|
||||
import { PayloadSegment } from '../../types/packet';
|
||||
import { DecryptionOptions } from '../../types/crypto';
|
||||
export declare class GroupTextPayloadDecoder {
|
||||
static decode(payload: Uint8Array, options?: DecryptionOptions & {
|
||||
includeSegments?: boolean;
|
||||
segmentOffset?: number;
|
||||
}): GroupTextPayload & {
|
||||
segments?: PayloadSegment[];
|
||||
} | null;
|
||||
}
|
||||
//# sourceMappingURL=group-text.d.ts.map
|
||||
1
frontend/lib/meshcore-decoder/dist/decoder/payload-decoders/group-text.d.ts.map
vendored
Normal file
1
frontend/lib/meshcore-decoder/dist/decoder/payload-decoders/group-text.d.ts.map
vendored
Normal file
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"group-text.d.ts","sourceRoot":"","sources":["../../../src/decoder/payload-decoders/group-text.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,gBAAgB,EAAE,MAAM,sBAAsB,CAAC;AACxD,OAAO,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AAEpD,OAAO,EAAE,iBAAiB,EAAE,MAAM,oBAAoB,CAAC;AAIvD,qBAAa,uBAAuB;IAClC,MAAM,CAAC,MAAM,CAAC,OAAO,EAAE,UAAU,EAAE,OAAO,CAAC,EAAE,iBAAiB,GAAG;QAAE,eAAe,CAAC,EAAE,OAAO,CAAC;QAAC,aAAa,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG,gBAAgB,GAAG;QAAE,QAAQ,CAAC,EAAE,cAAc,EAAE,CAAA;KAAE,GAAG,IAAI;CAyHnL"}
|
||||
118
frontend/lib/meshcore-decoder/dist/decoder/payload-decoders/group-text.js
vendored
Normal file
118
frontend/lib/meshcore-decoder/dist/decoder/payload-decoders/group-text.js
vendored
Normal file
@@ -0,0 +1,118 @@
|
||||
"use strict";
|
||||
// Copyright (c) 2025 Michael Hart: https://github.com/michaelhart/meshcore-decoder
|
||||
// MIT License
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.GroupTextPayloadDecoder = void 0;
|
||||
const enums_1 = require("../../types/enums");
|
||||
const channel_crypto_1 = require("../../crypto/channel-crypto");
|
||||
const hex_1 = require("../../utils/hex");
|
||||
class GroupTextPayloadDecoder {
|
||||
static decode(payload, options) {
|
||||
try {
|
||||
if (payload.length < 3) {
|
||||
const result = {
|
||||
type: enums_1.PayloadType.GroupText,
|
||||
version: enums_1.PayloadVersion.Version1,
|
||||
isValid: false,
|
||||
errors: ['GroupText payload too short (need at least channel_hash(1) + MAC(2))'],
|
||||
channelHash: '',
|
||||
cipherMac: '',
|
||||
ciphertext: '',
|
||||
ciphertextLength: 0
|
||||
};
|
||||
if (options?.includeSegments) {
|
||||
result.segments = [{
|
||||
name: 'Invalid GroupText Data',
|
||||
description: 'GroupText payload too short (minimum 3 bytes required)',
|
||||
startByte: options.segmentOffset || 0,
|
||||
endByte: (options.segmentOffset || 0) + payload.length - 1,
|
||||
value: (0, hex_1.bytesToHex)(payload)
|
||||
}];
|
||||
}
|
||||
return result;
|
||||
}
|
||||
const segments = [];
|
||||
const segmentOffset = options?.segmentOffset || 0;
|
||||
let offset = 0;
|
||||
// channel hash (1 byte) - first byte of SHA256 of channel's shared key
|
||||
const channelHash = (0, hex_1.byteToHex)(payload[offset]);
|
||||
if (options?.includeSegments) {
|
||||
segments.push({
|
||||
name: 'Channel Hash',
|
||||
description: 'First byte of SHA256 of channel\'s shared key',
|
||||
startByte: segmentOffset + offset,
|
||||
endByte: segmentOffset + offset,
|
||||
value: channelHash
|
||||
});
|
||||
}
|
||||
offset += 1;
|
||||
// MAC (2 bytes) - message authentication code
|
||||
const cipherMac = (0, hex_1.bytesToHex)(payload.subarray(offset, offset + 2));
|
||||
if (options?.includeSegments) {
|
||||
segments.push({
|
||||
name: 'Cipher MAC',
|
||||
description: 'MAC for encrypted data',
|
||||
startByte: segmentOffset + offset,
|
||||
endByte: segmentOffset + offset + 1,
|
||||
value: cipherMac
|
||||
});
|
||||
}
|
||||
offset += 2;
|
||||
// ciphertext (remaining bytes) - encrypted message
|
||||
const ciphertext = (0, hex_1.bytesToHex)(payload.subarray(offset));
|
||||
if (options?.includeSegments && payload.length > offset) {
|
||||
segments.push({
|
||||
name: 'Ciphertext',
|
||||
description: 'Encrypted message content (timestamp + flags + message)',
|
||||
startByte: segmentOffset + offset,
|
||||
endByte: segmentOffset + payload.length - 1,
|
||||
value: ciphertext
|
||||
});
|
||||
}
|
||||
const groupText = {
|
||||
type: enums_1.PayloadType.GroupText,
|
||||
version: enums_1.PayloadVersion.Version1,
|
||||
isValid: true,
|
||||
channelHash,
|
||||
cipherMac,
|
||||
ciphertext,
|
||||
ciphertextLength: payload.length - 3
|
||||
};
|
||||
// attempt decryption if key store is provided
|
||||
if (options?.keyStore && options.keyStore.hasChannelKey(channelHash)) {
|
||||
// try all possible keys for this hash (handles collisions)
|
||||
const channelKeys = options.keyStore.getChannelKeys(channelHash);
|
||||
for (const channelKey of channelKeys) {
|
||||
const decryptionResult = channel_crypto_1.ChannelCrypto.decryptGroupTextMessage(ciphertext, cipherMac, channelKey);
|
||||
if (decryptionResult.success && decryptionResult.data) {
|
||||
groupText.decrypted = {
|
||||
timestamp: decryptionResult.data.timestamp,
|
||||
flags: decryptionResult.data.flags,
|
||||
sender: decryptionResult.data.sender,
|
||||
message: decryptionResult.data.message
|
||||
};
|
||||
break; // stop trying keys once we find one that works
|
||||
}
|
||||
}
|
||||
}
|
||||
if (options?.includeSegments) {
|
||||
groupText.segments = segments;
|
||||
}
|
||||
return groupText;
|
||||
}
|
||||
catch (error) {
|
||||
return {
|
||||
type: enums_1.PayloadType.GroupText,
|
||||
version: enums_1.PayloadVersion.Version1,
|
||||
isValid: false,
|
||||
errors: [error instanceof Error ? error.message : 'Failed to decode GroupText payload'],
|
||||
channelHash: '',
|
||||
cipherMac: '',
|
||||
ciphertext: '',
|
||||
ciphertextLength: 0
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
exports.GroupTextPayloadDecoder = GroupTextPayloadDecoder;
|
||||
//# sourceMappingURL=group-text.js.map
|
||||
1
frontend/lib/meshcore-decoder/dist/decoder/payload-decoders/group-text.js.map
vendored
Normal file
1
frontend/lib/meshcore-decoder/dist/decoder/payload-decoders/group-text.js.map
vendored
Normal file
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"group-text.js","sourceRoot":"","sources":["../../../src/decoder/payload-decoders/group-text.ts"],"names":[],"mappings":"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"}
|
||||
5
frontend/lib/meshcore-decoder/dist/decoder/payload-decoders/path.d.ts
vendored
Normal file
5
frontend/lib/meshcore-decoder/dist/decoder/payload-decoders/path.d.ts
vendored
Normal file
@@ -0,0 +1,5 @@
|
||||
import { PathPayload } from '../../types/payloads';
|
||||
export declare class PathPayloadDecoder {
|
||||
static decode(payload: Uint8Array): PathPayload | null;
|
||||
}
|
||||
//# sourceMappingURL=path.d.ts.map
|
||||
1
frontend/lib/meshcore-decoder/dist/decoder/payload-decoders/path.d.ts.map
vendored
Normal file
1
frontend/lib/meshcore-decoder/dist/decoder/payload-decoders/path.d.ts.map
vendored
Normal file
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"path.d.ts","sourceRoot":"","sources":["../../../src/decoder/payload-decoders/path.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,WAAW,EAAE,MAAM,sBAAsB,CAAC;AAInD,qBAAa,kBAAkB;IAC7B,MAAM,CAAC,MAAM,CAAC,OAAO,EAAE,UAAU,GAAG,WAAW,GAAG,IAAI;CA6FvD"}
|
||||
97
frontend/lib/meshcore-decoder/dist/decoder/payload-decoders/path.js
vendored
Normal file
97
frontend/lib/meshcore-decoder/dist/decoder/payload-decoders/path.js
vendored
Normal file
@@ -0,0 +1,97 @@
|
||||
"use strict";
|
||||
// Copyright (c) 2025 Michael Hart: https://github.com/michaelhart/meshcore-decoder
|
||||
// MIT License
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.PathPayloadDecoder = void 0;
|
||||
const enums_1 = require("../../types/enums");
|
||||
const hex_1 = require("../../utils/hex");
|
||||
class PathPayloadDecoder {
|
||||
static decode(payload) {
|
||||
try {
|
||||
// Based on MeshCore payloads.md - Path payload structure:
|
||||
// - path_len (1 byte, encoded: bits 7:6 = hash size selector, bits 5:0 = hop count)
|
||||
// - path (variable length) - list of node hashes (pathHashSize bytes each)
|
||||
// - extra_type (1 byte) - bundled payload type
|
||||
// - extra (rest of data) - bundled payload content
|
||||
if (payload.length < 2) {
|
||||
return {
|
||||
type: enums_1.PayloadType.Path,
|
||||
version: enums_1.PayloadVersion.Version1,
|
||||
isValid: false,
|
||||
errors: ['Path payload too short (minimum 2 bytes: path length + extra type)'],
|
||||
pathLength: 0,
|
||||
pathHashes: [],
|
||||
extraType: 0,
|
||||
extraData: ''
|
||||
};
|
||||
}
|
||||
const pathLenByte = payload[0];
|
||||
const pathHashSize = (pathLenByte >> 6) + 1;
|
||||
const pathHopCount = pathLenByte & 63;
|
||||
const pathByteLength = pathHopCount * pathHashSize;
|
||||
if (pathHashSize === 4) {
|
||||
return {
|
||||
type: enums_1.PayloadType.Path,
|
||||
version: enums_1.PayloadVersion.Version1,
|
||||
isValid: false,
|
||||
errors: ['Invalid path length byte: reserved hash size (bits 7:6 = 11)'],
|
||||
pathLength: 0,
|
||||
pathHashes: [],
|
||||
extraType: 0,
|
||||
extraData: ''
|
||||
};
|
||||
}
|
||||
if (payload.length < 1 + pathByteLength + 1) {
|
||||
return {
|
||||
type: enums_1.PayloadType.Path,
|
||||
version: enums_1.PayloadVersion.Version1,
|
||||
isValid: false,
|
||||
errors: [`Path payload too short (need ${1 + pathByteLength + 1} bytes for path length + path + extra type)`],
|
||||
pathLength: pathHopCount,
|
||||
...(pathHashSize > 1 ? { pathHashSize } : {}),
|
||||
pathHashes: [],
|
||||
extraType: 0,
|
||||
extraData: ''
|
||||
};
|
||||
}
|
||||
// Parse path hashes (pathHashSize bytes each)
|
||||
const pathHashes = [];
|
||||
for (let i = 0; i < pathHopCount; i++) {
|
||||
const hashStart = 1 + i * pathHashSize;
|
||||
const hashBytes = payload.subarray(hashStart, hashStart + pathHashSize);
|
||||
pathHashes.push((0, hex_1.bytesToHex)(hashBytes));
|
||||
}
|
||||
// Parse extra type (1 byte after path)
|
||||
const extraType = payload[1 + pathByteLength];
|
||||
// Parse extra data (remaining bytes)
|
||||
let extraData = '';
|
||||
if (payload.length > 1 + pathByteLength + 1) {
|
||||
extraData = (0, hex_1.bytesToHex)(payload.subarray(1 + pathByteLength + 1));
|
||||
}
|
||||
return {
|
||||
type: enums_1.PayloadType.Path,
|
||||
version: enums_1.PayloadVersion.Version1,
|
||||
isValid: true,
|
||||
pathLength: pathHopCount,
|
||||
...(pathHashSize > 1 ? { pathHashSize } : {}),
|
||||
pathHashes,
|
||||
extraType,
|
||||
extraData
|
||||
};
|
||||
}
|
||||
catch (error) {
|
||||
return {
|
||||
type: enums_1.PayloadType.Path,
|
||||
version: enums_1.PayloadVersion.Version1,
|
||||
isValid: false,
|
||||
errors: [error instanceof Error ? error.message : 'Failed to decode Path payload'],
|
||||
pathLength: 0,
|
||||
pathHashes: [],
|
||||
extraType: 0,
|
||||
extraData: ''
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
exports.PathPayloadDecoder = PathPayloadDecoder;
|
||||
//# sourceMappingURL=path.js.map
|
||||
1
frontend/lib/meshcore-decoder/dist/decoder/payload-decoders/path.js.map
vendored
Normal file
1
frontend/lib/meshcore-decoder/dist/decoder/payload-decoders/path.js.map
vendored
Normal file
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"path.js","sourceRoot":"","sources":["../../../src/decoder/payload-decoders/path.ts"],"names":[],"mappings":";AAAA,mFAAmF;AACnF,cAAc;;;AAGd,6CAAgE;AAChE,yCAA6C;AAE7C,MAAa,kBAAkB;IAC7B,MAAM,CAAC,MAAM,CAAC,OAAmB;QAC/B,IAAI,CAAC;YACH,0DAA0D;YAC1D,oFAAoF;YACpF,2EAA2E;YAC3E,+CAA+C;YAC/C,mDAAmD;YAEnD,IAAI,OAAO,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;gBACvB,OAAO;oBACL,IAAI,EAAE,mBAAW,CAAC,IAAI;oBACtB,OAAO,EAAE,sBAAc,CAAC,QAAQ;oBAChC,OAAO,EAAE,KAAK;oBACd,MAAM,EAAE,CAAC,oEAAoE,CAAC;oBAC9E,UAAU,EAAE,CAAC;oBACb,UAAU,EAAE,EAAE;oBACd,SAAS,EAAE,CAAC;oBACZ,SAAS,EAAE,EAAE;iBACd,CAAC;YACJ,CAAC;YAED,MAAM,WAAW,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC;YAC/B,MAAM,YAAY,GAAG,CAAC,WAAW,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC;YAC5C,MAAM,YAAY,GAAG,WAAW,GAAG,EAAE,CAAC;YACtC,MAAM,cAAc,GAAG,YAAY,GAAG,YAAY,CAAC;YAEnD,IAAI,YAAY,KAAK,CAAC,EAAE,CAAC;gBACvB,OAAO;oBACL,IAAI,EAAE,mBAAW,CAAC,IAAI;oBACtB,OAAO,EAAE,sBAAc,CAAC,QAAQ;oBAChC,OAAO,EAAE,KAAK;oBACd,MAAM,EAAE,CAAC,8DAA8D,CAAC;oBACxE,UAAU,EAAE,CAAC;oBACb,UAAU,EAAE,EAAE;oBACd,SAAS,EAAE,CAAC;oBACZ,SAAS,EAAE,EAAE;iBACd,CAAC;YACJ,CAAC;YAED,IAAI,OAAO,CAAC,MAAM,GAAG,CAAC,GAAG,cAAc,GAAG,CAAC,EAAE,CAAC;gBAC5C,OAAO;oBACL,IAAI,EAAE,mBAAW,CAAC,IAAI;oBACtB,OAAO,EAAE,sBAAc,CAAC,QAAQ;oBAChC,OAAO,EAAE,KAAK;oBACd,MAAM,EAAE,CAAC,gCAAgC,CAAC,GAAG,cAAc,GAAG,CAAC,6CAA6C,CAAC;oBAC7G,UAAU,EAAE,YAAY;oBACxB,GAAG,CAAC,YAAY,GAAG,CAAC,CAAC,CAAC,CAAC,EAAE,YAAY,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;oBAC7C,UAAU,EAAE,EAAE;oBACd,SAAS,EAAE,CAAC;oBACZ,SAAS,EAAE,EAAE;iBACd,CAAC;YACJ,CAAC;YAED,8CAA8C;YAC9C,MAAM,UAAU,GAAa,EAAE,CAAC;YAChC,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,YAAY,EAAE,CAAC,EAAE,EAAE,CAAC;gBACtC,MAAM,SAAS,GAAG,CAAC,GAAG,CAAC,GAAG,YAAY,CAAC;gBACvC,MAAM,SAAS,GAAG,OAAO,CAAC,QAAQ,CAAC,SAAS,EAAE,SAAS,GAAG,YAAY,CAAC,CAAC;gBACxE,UAAU,CAAC,IAAI,CAAC,IAAA,gBAAU,EAAC,SAAS,CAAC,CAAC,CAAC;YACzC,CAAC;YAED,uCAAuC;YACvC,MAAM,SAAS,GAAG,OAAO,CAAC,CAAC,GAAG,cAAc,CAAC,CAAC;YAE9C,qCAAqC;YACrC,IAAI,SAAS,GAAG,EAAE,CAAC;YACnB,IAAI,OAAO,CAAC,MAAM,GAAG,CAAC,GAAG,cAAc,GAAG,CAAC,EAAE,CAAC;gBAC5C,SAAS,GAAG,IAAA,gBAAU,EAAC,OAAO,CAAC,QAAQ,CAAC,CAAC,GAAG,cAAc,GAAG,CAAC,CAAC,CAAC,CAAC;YACnE,CAAC;YAED,OAAO;gBACL,IAAI,EAAE,mBAAW,CAAC,IAAI;gBACtB,OAAO,EAAE,sBAAc,CAAC,QAAQ;gBAChC,OAAO,EAAE,IAAI;gBACb,UAAU,EAAE,YAAY;gBACxB,GAAG,CAAC,YAAY,GAAG,CAAC,CAAC,CAAC,CAAC,EAAE,YAAY,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;gBAC7C,UAAU;gBACV,SAAS;gBACT,SAAS;aACV,CAAC;QACJ,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,OAAO;gBACL,IAAI,EAAE,mBAAW,CAAC,IAAI;gBACtB,OAAO,EAAE,sBAAc,CAAC,QAAQ;gBAChC,OAAO,EAAE,KAAK;gBACd,MAAM,EAAE,CAAC,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,+BAA+B,CAAC;gBAClF,UAAU,EAAE,CAAC;gBACb,UAAU,EAAE,EAAE;gBACd,SAAS,EAAE,CAAC;gBACZ,SAAS,EAAE,EAAE;aACd,CAAC;QACJ,CAAC;IACH,CAAC;CACF;AA9FD,gDA8FC"}
|
||||
11
frontend/lib/meshcore-decoder/dist/decoder/payload-decoders/request.d.ts
vendored
Normal file
11
frontend/lib/meshcore-decoder/dist/decoder/payload-decoders/request.d.ts
vendored
Normal file
@@ -0,0 +1,11 @@
|
||||
import { RequestPayload } from '../../types/payloads';
|
||||
import { PayloadSegment } from '../../types/packet';
|
||||
export declare class RequestPayloadDecoder {
|
||||
static decode(payload: Uint8Array, options?: {
|
||||
includeSegments?: boolean;
|
||||
segmentOffset?: number;
|
||||
}): RequestPayload & {
|
||||
segments?: PayloadSegment[];
|
||||
} | null;
|
||||
}
|
||||
//# sourceMappingURL=request.d.ts.map
|
||||
1
frontend/lib/meshcore-decoder/dist/decoder/payload-decoders/request.d.ts.map
vendored
Normal file
1
frontend/lib/meshcore-decoder/dist/decoder/payload-decoders/request.d.ts.map
vendored
Normal file
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"request.d.ts","sourceRoot":"","sources":["../../../src/decoder/payload-decoders/request.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AACtD,OAAO,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AAIpD,qBAAa,qBAAqB;IAChC,MAAM,CAAC,MAAM,CAAC,OAAO,EAAE,UAAU,EAAE,OAAO,CAAC,EAAE;QAAE,eAAe,CAAC,EAAE,OAAO,CAAC;QAAC,aAAa,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG,cAAc,GAAG;QAAE,QAAQ,CAAC,EAAE,cAAc,EAAE,CAAA;KAAE,GAAG,IAAI;CAqI7J"}
|
||||
129
frontend/lib/meshcore-decoder/dist/decoder/payload-decoders/request.js
vendored
Normal file
129
frontend/lib/meshcore-decoder/dist/decoder/payload-decoders/request.js
vendored
Normal file
@@ -0,0 +1,129 @@
|
||||
"use strict";
|
||||
// Copyright (c) 2025 Michael Hart: https://github.com/michaelhart/meshcore-decoder
|
||||
// MIT License
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.RequestPayloadDecoder = void 0;
|
||||
const enums_1 = require("../../types/enums");
|
||||
const hex_1 = require("../../utils/hex");
|
||||
class RequestPayloadDecoder {
|
||||
static decode(payload, options) {
|
||||
try {
|
||||
// Based on MeshCore payloads.md - Request payload structure:
|
||||
// - destination hash (1 byte)
|
||||
// - source hash (1 byte)
|
||||
// - cipher MAC (2 bytes)
|
||||
// - ciphertext (rest of payload) - contains encrypted timestamp, request type, and request data
|
||||
if (payload.length < 4) {
|
||||
const result = {
|
||||
type: enums_1.PayloadType.Request,
|
||||
version: enums_1.PayloadVersion.Version1,
|
||||
isValid: false,
|
||||
errors: ['Request payload too short (minimum 4 bytes: dest hash + source hash + MAC)'],
|
||||
timestamp: 0,
|
||||
requestType: enums_1.RequestType.GetStats,
|
||||
requestData: '',
|
||||
destinationHash: '',
|
||||
sourceHash: '',
|
||||
cipherMac: '',
|
||||
ciphertext: ''
|
||||
};
|
||||
if (options?.includeSegments) {
|
||||
result.segments = [{
|
||||
name: 'Invalid Request Data',
|
||||
description: 'Request payload too short (minimum 4 bytes required: 1 for dest hash + 1 for source hash + 2 for MAC)',
|
||||
startByte: options.segmentOffset || 0,
|
||||
endByte: (options.segmentOffset || 0) + payload.length - 1,
|
||||
value: (0, hex_1.bytesToHex)(payload)
|
||||
}];
|
||||
}
|
||||
return result;
|
||||
}
|
||||
const segments = [];
|
||||
const segmentOffset = options?.segmentOffset || 0;
|
||||
let offset = 0;
|
||||
// Parse destination hash (1 byte)
|
||||
const destinationHash = (0, hex_1.bytesToHex)(payload.subarray(offset, offset + 1));
|
||||
if (options?.includeSegments) {
|
||||
segments.push({
|
||||
name: 'Destination Hash',
|
||||
description: `First byte of destination node public key: 0x${destinationHash}`,
|
||||
startByte: segmentOffset + offset,
|
||||
endByte: segmentOffset + offset,
|
||||
value: destinationHash
|
||||
});
|
||||
}
|
||||
offset += 1;
|
||||
// Parse source hash (1 byte)
|
||||
const sourceHash = (0, hex_1.bytesToHex)(payload.subarray(offset, offset + 1));
|
||||
if (options?.includeSegments) {
|
||||
segments.push({
|
||||
name: 'Source Hash',
|
||||
description: `First byte of source node public key: 0x${sourceHash}`,
|
||||
startByte: segmentOffset + offset,
|
||||
endByte: segmentOffset + offset,
|
||||
value: sourceHash
|
||||
});
|
||||
}
|
||||
offset += 1;
|
||||
// Parse cipher MAC (2 bytes)
|
||||
const cipherMac = (0, hex_1.bytesToHex)(payload.subarray(offset, offset + 2));
|
||||
if (options?.includeSegments) {
|
||||
segments.push({
|
||||
name: 'Cipher MAC',
|
||||
description: `MAC for encrypted data verification (2 bytes)`,
|
||||
startByte: segmentOffset + offset,
|
||||
endByte: segmentOffset + offset + 1,
|
||||
value: cipherMac
|
||||
});
|
||||
}
|
||||
offset += 2;
|
||||
// Parse ciphertext (remaining bytes)
|
||||
const ciphertext = (0, hex_1.bytesToHex)(payload.subarray(offset));
|
||||
if (options?.includeSegments && payload.length > offset) {
|
||||
segments.push({
|
||||
name: 'Ciphertext',
|
||||
description: `Encrypted message data (${payload.length - offset} bytes). Contains encrypted plaintext with this structure:
|
||||
• Timestamp (4 bytes) - send time as unix timestamp
|
||||
• Request Type (1 byte) - type of request (GetStats, GetTelemetryData, etc.)
|
||||
• Request Data (remaining bytes) - additional request-specific data`,
|
||||
startByte: segmentOffset + offset,
|
||||
endByte: segmentOffset + payload.length - 1,
|
||||
value: ciphertext
|
||||
});
|
||||
}
|
||||
const result = {
|
||||
type: enums_1.PayloadType.Request,
|
||||
version: enums_1.PayloadVersion.Version1,
|
||||
isValid: true,
|
||||
timestamp: 0, // Encrypted, cannot be parsed without decryption
|
||||
requestType: enums_1.RequestType.GetStats, // Encrypted, cannot be determined without decryption
|
||||
requestData: '',
|
||||
destinationHash,
|
||||
sourceHash,
|
||||
cipherMac,
|
||||
ciphertext
|
||||
};
|
||||
if (options?.includeSegments) {
|
||||
result.segments = segments;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
catch (error) {
|
||||
return {
|
||||
type: enums_1.PayloadType.Request,
|
||||
version: enums_1.PayloadVersion.Version1,
|
||||
isValid: false,
|
||||
errors: [error instanceof Error ? error.message : 'Failed to decode request payload'],
|
||||
timestamp: 0,
|
||||
requestType: enums_1.RequestType.GetStats,
|
||||
requestData: '',
|
||||
destinationHash: '',
|
||||
sourceHash: '',
|
||||
cipherMac: '',
|
||||
ciphertext: ''
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
exports.RequestPayloadDecoder = RequestPayloadDecoder;
|
||||
//# sourceMappingURL=request.js.map
|
||||
1
frontend/lib/meshcore-decoder/dist/decoder/payload-decoders/request.js.map
vendored
Normal file
1
frontend/lib/meshcore-decoder/dist/decoder/payload-decoders/request.js.map
vendored
Normal file
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"request.js","sourceRoot":"","sources":["../../../src/decoder/payload-decoders/request.ts"],"names":[],"mappings":"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"}
|
||||
11
frontend/lib/meshcore-decoder/dist/decoder/payload-decoders/response.d.ts
vendored
Normal file
11
frontend/lib/meshcore-decoder/dist/decoder/payload-decoders/response.d.ts
vendored
Normal file
@@ -0,0 +1,11 @@
|
||||
import { ResponsePayload } from '../../types/payloads';
|
||||
import { PayloadSegment } from '../../types/packet';
|
||||
export declare class ResponsePayloadDecoder {
|
||||
static decode(payload: Uint8Array, options?: {
|
||||
includeSegments?: boolean;
|
||||
segmentOffset?: number;
|
||||
}): ResponsePayload & {
|
||||
segments?: PayloadSegment[];
|
||||
} | null;
|
||||
}
|
||||
//# sourceMappingURL=response.d.ts.map
|
||||
1
frontend/lib/meshcore-decoder/dist/decoder/payload-decoders/response.d.ts.map
vendored
Normal file
1
frontend/lib/meshcore-decoder/dist/decoder/payload-decoders/response.d.ts.map
vendored
Normal file
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"response.d.ts","sourceRoot":"","sources":["../../../src/decoder/payload-decoders/response.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAC;AACvD,OAAO,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AAIpD,qBAAa,sBAAsB;IACjC,MAAM,CAAC,MAAM,CAAC,OAAO,EAAE,UAAU,EAAE,OAAO,CAAC,EAAE;QAAE,eAAe,CAAC,EAAE,OAAO,CAAC;QAAC,aAAa,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG,eAAe,GAAG;QAAE,QAAQ,CAAC,EAAE,cAAc,EAAE,CAAA;KAAE,GAAG,IAAI;CAuH9J"}
|
||||
120
frontend/lib/meshcore-decoder/dist/decoder/payload-decoders/response.js
vendored
Normal file
120
frontend/lib/meshcore-decoder/dist/decoder/payload-decoders/response.js
vendored
Normal file
@@ -0,0 +1,120 @@
|
||||
"use strict";
|
||||
// Copyright (c) 2025 Michael Hart: https://github.com/michaelhart/meshcore-decoder
|
||||
// MIT License
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.ResponsePayloadDecoder = void 0;
|
||||
const enums_1 = require("../../types/enums");
|
||||
const hex_1 = require("../../utils/hex");
|
||||
class ResponsePayloadDecoder {
|
||||
static decode(payload, options) {
|
||||
try {
|
||||
// Based on MeshCore payloads.md - Response payload structure:
|
||||
// - destination_hash (1 byte)
|
||||
// - source_hash (1 byte)
|
||||
// - cipher_mac (2 bytes)
|
||||
// - ciphertext (rest of payload)
|
||||
if (payload.length < 4) {
|
||||
const result = {
|
||||
type: enums_1.PayloadType.Response,
|
||||
version: enums_1.PayloadVersion.Version1,
|
||||
isValid: false,
|
||||
errors: ['Response payload too short (minimum 4 bytes: dest + source + MAC)'],
|
||||
destinationHash: '',
|
||||
sourceHash: '',
|
||||
cipherMac: '',
|
||||
ciphertext: '',
|
||||
ciphertextLength: 0
|
||||
};
|
||||
if (options?.includeSegments) {
|
||||
result.segments = [{
|
||||
name: 'Invalid Response Data',
|
||||
description: 'Response payload too short (minimum 4 bytes required)',
|
||||
startByte: options.segmentOffset || 0,
|
||||
endByte: (options.segmentOffset || 0) + payload.length - 1,
|
||||
value: (0, hex_1.bytesToHex)(payload)
|
||||
}];
|
||||
}
|
||||
return result;
|
||||
}
|
||||
const segments = [];
|
||||
const segmentOffset = options?.segmentOffset || 0;
|
||||
let offset = 0;
|
||||
// Destination Hash (1 byte)
|
||||
const destinationHash = (0, hex_1.byteToHex)(payload[offset]);
|
||||
if (options?.includeSegments) {
|
||||
segments.push({
|
||||
name: 'Destination Hash',
|
||||
description: 'First byte of destination node public key',
|
||||
startByte: segmentOffset + offset,
|
||||
endByte: segmentOffset + offset,
|
||||
value: destinationHash
|
||||
});
|
||||
}
|
||||
offset += 1;
|
||||
// source hash (1 byte)
|
||||
const sourceHash = (0, hex_1.byteToHex)(payload[offset]);
|
||||
if (options?.includeSegments) {
|
||||
segments.push({
|
||||
name: 'Source Hash',
|
||||
description: 'First byte of source node public key',
|
||||
startByte: segmentOffset + offset,
|
||||
endByte: segmentOffset + offset,
|
||||
value: sourceHash
|
||||
});
|
||||
}
|
||||
offset += 1;
|
||||
// cipher MAC (2 bytes)
|
||||
const cipherMac = (0, hex_1.bytesToHex)(payload.subarray(offset, offset + 2));
|
||||
if (options?.includeSegments) {
|
||||
segments.push({
|
||||
name: 'Cipher MAC',
|
||||
description: 'MAC for encrypted data in next field',
|
||||
startByte: segmentOffset + offset,
|
||||
endByte: segmentOffset + offset + 1,
|
||||
value: cipherMac
|
||||
});
|
||||
}
|
||||
offset += 2;
|
||||
// ciphertext (remaining bytes)
|
||||
const ciphertext = (0, hex_1.bytesToHex)(payload.subarray(offset));
|
||||
if (options?.includeSegments && payload.length > offset) {
|
||||
segments.push({
|
||||
name: 'Ciphertext',
|
||||
description: 'Encrypted response data (tag + content)',
|
||||
startByte: segmentOffset + offset,
|
||||
endByte: segmentOffset + payload.length - 1,
|
||||
value: ciphertext
|
||||
});
|
||||
}
|
||||
const result = {
|
||||
type: enums_1.PayloadType.Response,
|
||||
version: enums_1.PayloadVersion.Version1,
|
||||
isValid: true,
|
||||
destinationHash,
|
||||
sourceHash,
|
||||
cipherMac,
|
||||
ciphertext,
|
||||
ciphertextLength: payload.length - 4
|
||||
};
|
||||
if (options?.includeSegments) {
|
||||
result.segments = segments;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
catch (error) {
|
||||
return {
|
||||
type: enums_1.PayloadType.Response,
|
||||
version: enums_1.PayloadVersion.Version1,
|
||||
isValid: false,
|
||||
errors: [error instanceof Error ? error.message : 'Failed to decode response payload'],
|
||||
destinationHash: '',
|
||||
sourceHash: '',
|
||||
cipherMac: '',
|
||||
ciphertext: '',
|
||||
ciphertextLength: 0
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
exports.ResponsePayloadDecoder = ResponsePayloadDecoder;
|
||||
//# sourceMappingURL=response.js.map
|
||||
1
frontend/lib/meshcore-decoder/dist/decoder/payload-decoders/response.js.map
vendored
Normal file
1
frontend/lib/meshcore-decoder/dist/decoder/payload-decoders/response.js.map
vendored
Normal file
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"response.js","sourceRoot":"","sources":["../../../src/decoder/payload-decoders/response.ts"],"names":[],"mappings":";AAAA,mFAAmF;AACnF,cAAc;;;AAId,6CAAgE;AAChE,yCAAwD;AAExD,MAAa,sBAAsB;IACjC,MAAM,CAAC,MAAM,CAAC,OAAmB,EAAE,OAA+D;QAChG,IAAI,CAAC;YACH,8DAA8D;YAC9D,8BAA8B;YAC9B,yBAAyB;YACzB,yBAAyB;YACzB,iCAAiC;YAEjC,IAAI,OAAO,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;gBACvB,MAAM,MAAM,GAAsD;oBAChE,IAAI,EAAE,mBAAW,CAAC,QAAQ;oBAC1B,OAAO,EAAE,sBAAc,CAAC,QAAQ;oBAChC,OAAO,EAAE,KAAK;oBACd,MAAM,EAAE,CAAC,mEAAmE,CAAC;oBAC7E,eAAe,EAAE,EAAE;oBACnB,UAAU,EAAE,EAAE;oBACd,SAAS,EAAE,EAAE;oBACb,UAAU,EAAE,EAAE;oBACd,gBAAgB,EAAE,CAAC;iBACpB,CAAC;gBAEF,IAAI,OAAO,EAAE,eAAe,EAAE,CAAC;oBAC7B,MAAM,CAAC,QAAQ,GAAG,CAAC;4BACjB,IAAI,EAAE,uBAAuB;4BAC7B,WAAW,EAAE,uDAAuD;4BACpE,SAAS,EAAE,OAAO,CAAC,aAAa,IAAI,CAAC;4BACrC,OAAO,EAAE,CAAC,OAAO,CAAC,aAAa,IAAI,CAAC,CAAC,GAAG,OAAO,CAAC,MAAM,GAAG,CAAC;4BAC1D,KAAK,EAAE,IAAA,gBAAU,EAAC,OAAO,CAAC;yBAC3B,CAAC,CAAC;gBACL,CAAC;gBAED,OAAO,MAAM,CAAC;YAChB,CAAC;YAED,MAAM,QAAQ,GAAqB,EAAE,CAAC;YACtC,MAAM,aAAa,GAAG,OAAO,EAAE,aAAa,IAAI,CAAC,CAAC;YAClD,IAAI,MAAM,GAAG,CAAC,CAAC;YAEf,4BAA4B;YAC5B,MAAM,eAAe,GAAG,IAAA,eAAS,EAAC,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC;YACnD,IAAI,OAAO,EAAE,eAAe,EAAE,CAAC;gBAC7B,QAAQ,CAAC,IAAI,CAAC;oBACZ,IAAI,EAAE,kBAAkB;oBACxB,WAAW,EAAE,2CAA2C;oBACxD,SAAS,EAAE,aAAa,GAAG,MAAM;oBACjC,OAAO,EAAE,aAAa,GAAG,MAAM;oBAC/B,KAAK,EAAE,eAAe;iBACvB,CAAC,CAAC;YACL,CAAC;YACD,MAAM,IAAI,CAAC,CAAC;YAEZ,uBAAuB;YACvB,MAAM,UAAU,GAAG,IAAA,eAAS,EAAC,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC;YAC9C,IAAI,OAAO,EAAE,eAAe,EAAE,CAAC;gBAC7B,QAAQ,CAAC,IAAI,CAAC;oBACZ,IAAI,EAAE,aAAa;oBACnB,WAAW,EAAE,sCAAsC;oBACnD,SAAS,EAAE,aAAa,GAAG,MAAM;oBACjC,OAAO,EAAE,aAAa,GAAG,MAAM;oBAC/B,KAAK,EAAE,UAAU;iBAClB,CAAC,CAAC;YACL,CAAC;YACD,MAAM,IAAI,CAAC,CAAC;YAEZ,uBAAuB;YACvB,MAAM,SAAS,GAAG,IAAA,gBAAU,EAAC,OAAO,CAAC,QAAQ,CAAC,MAAM,EAAE,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC;YACnE,IAAI,OAAO,EAAE,eAAe,EAAE,CAAC;gBAC7B,QAAQ,CAAC,IAAI,CAAC;oBACZ,IAAI,EAAE,YAAY;oBAClB,WAAW,EAAE,sCAAsC;oBACnD,SAAS,EAAE,aAAa,GAAG,MAAM;oBACjC,OAAO,EAAE,aAAa,GAAG,MAAM,GAAG,CAAC;oBACnC,KAAK,EAAE,SAAS;iBACjB,CAAC,CAAC;YACL,CAAC;YACD,MAAM,IAAI,CAAC,CAAC;YAEZ,+BAA+B;YAC/B,MAAM,UAAU,GAAG,IAAA,gBAAU,EAAC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC;YACxD,IAAI,OAAO,EAAE,eAAe,IAAI,OAAO,CAAC,MAAM,GAAG,MAAM,EAAE,CAAC;gBACxD,QAAQ,CAAC,IAAI,CAAC;oBACZ,IAAI,EAAE,YAAY;oBAClB,WAAW,EAAE,yCAAyC;oBACtD,SAAS,EAAE,aAAa,GAAG,MAAM;oBACjC,OAAO,EAAE,aAAa,GAAG,OAAO,CAAC,MAAM,GAAG,CAAC;oBAC3C,KAAK,EAAE,UAAU;iBAClB,CAAC,CAAC;YACL,CAAC;YAED,MAAM,MAAM,GAAsD;gBAChE,IAAI,EAAE,mBAAW,CAAC,QAAQ;gBAC1B,OAAO,EAAE,sBAAc,CAAC,QAAQ;gBAChC,OAAO,EAAE,IAAI;gBACb,eAAe;gBACf,UAAU;gBACV,SAAS;gBACT,UAAU;gBACV,gBAAgB,EAAE,OAAO,CAAC,MAAM,GAAG,CAAC;aACrC,CAAC;YAEF,IAAI,OAAO,EAAE,eAAe,EAAE,CAAC;gBAC7B,MAAM,CAAC,QAAQ,GAAG,QAAQ,CAAC;YAC7B,CAAC;YAED,OAAO,MAAM,CAAC;QAChB,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,OAAO;gBACL,IAAI,EAAE,mBAAW,CAAC,QAAQ;gBAC1B,OAAO,EAAE,sBAAc,CAAC,QAAQ;gBAChC,OAAO,EAAE,KAAK;gBACd,MAAM,EAAE,CAAC,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,mCAAmC,CAAC;gBACtF,eAAe,EAAE,EAAE;gBACnB,UAAU,EAAE,EAAE;gBACd,SAAS,EAAE,EAAE;gBACb,UAAU,EAAE,EAAE;gBACd,gBAAgB,EAAE,CAAC;aACpB,CAAC;QACJ,CAAC;IACH,CAAC;CACF;AAxHD,wDAwHC"}
|
||||
11
frontend/lib/meshcore-decoder/dist/decoder/payload-decoders/text-message.d.ts
vendored
Normal file
11
frontend/lib/meshcore-decoder/dist/decoder/payload-decoders/text-message.d.ts
vendored
Normal file
@@ -0,0 +1,11 @@
|
||||
import { TextMessagePayload } from '../../types/payloads';
|
||||
import { PayloadSegment } from '../../types/packet';
|
||||
export declare class TextMessagePayloadDecoder {
|
||||
static decode(payload: Uint8Array, options?: {
|
||||
includeSegments?: boolean;
|
||||
segmentOffset?: number;
|
||||
}): TextMessagePayload & {
|
||||
segments?: PayloadSegment[];
|
||||
} | null;
|
||||
}
|
||||
//# sourceMappingURL=text-message.d.ts.map
|
||||
1
frontend/lib/meshcore-decoder/dist/decoder/payload-decoders/text-message.d.ts.map
vendored
Normal file
1
frontend/lib/meshcore-decoder/dist/decoder/payload-decoders/text-message.d.ts.map
vendored
Normal file
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"text-message.d.ts","sourceRoot":"","sources":["../../../src/decoder/payload-decoders/text-message.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,kBAAkB,EAAE,MAAM,sBAAsB,CAAC;AAC1D,OAAO,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AAIpD,qBAAa,yBAAyB;IACpC,MAAM,CAAC,MAAM,CAAC,OAAO,EAAE,UAAU,EAAE,OAAO,CAAC,EAAE;QAAE,eAAe,CAAC,EAAE,OAAO,CAAC;QAAC,aAAa,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG,kBAAkB,GAAG;QAAE,QAAQ,CAAC,EAAE,cAAc,EAAE,CAAA;KAAE,GAAG,IAAI;CAuHjK"}
|
||||
120
frontend/lib/meshcore-decoder/dist/decoder/payload-decoders/text-message.js
vendored
Normal file
120
frontend/lib/meshcore-decoder/dist/decoder/payload-decoders/text-message.js
vendored
Normal file
@@ -0,0 +1,120 @@
|
||||
"use strict";
|
||||
// Copyright (c) 2025 Michael Hart: https://github.com/michaelhart/meshcore-decoder
|
||||
// MIT License
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.TextMessagePayloadDecoder = void 0;
|
||||
const enums_1 = require("../../types/enums");
|
||||
const hex_1 = require("../../utils/hex");
|
||||
class TextMessagePayloadDecoder {
|
||||
static decode(payload, options) {
|
||||
try {
|
||||
// Based on MeshCore payloads.md - TextMessage payload structure:
|
||||
// - destination_hash (1 byte)
|
||||
// - source_hash (1 byte)
|
||||
// - cipher_mac (2 bytes)
|
||||
// - ciphertext (rest of payload)
|
||||
if (payload.length < 4) {
|
||||
const result = {
|
||||
type: enums_1.PayloadType.TextMessage,
|
||||
version: enums_1.PayloadVersion.Version1,
|
||||
isValid: false,
|
||||
errors: ['TextMessage payload too short (minimum 4 bytes: dest + source + MAC)'],
|
||||
destinationHash: '',
|
||||
sourceHash: '',
|
||||
cipherMac: '',
|
||||
ciphertext: '',
|
||||
ciphertextLength: 0
|
||||
};
|
||||
if (options?.includeSegments) {
|
||||
result.segments = [{
|
||||
name: 'Invalid TextMessage Data',
|
||||
description: 'TextMessage payload too short (minimum 4 bytes required)',
|
||||
startByte: options.segmentOffset || 0,
|
||||
endByte: (options.segmentOffset || 0) + payload.length - 1,
|
||||
value: (0, hex_1.bytesToHex)(payload)
|
||||
}];
|
||||
}
|
||||
return result;
|
||||
}
|
||||
const segments = [];
|
||||
const segmentOffset = options?.segmentOffset || 0;
|
||||
let offset = 0;
|
||||
// Destination Hash (1 byte)
|
||||
const destinationHash = (0, hex_1.byteToHex)(payload[offset]);
|
||||
if (options?.includeSegments) {
|
||||
segments.push({
|
||||
name: 'Destination Hash',
|
||||
description: 'First byte of destination node public key',
|
||||
startByte: segmentOffset + offset,
|
||||
endByte: segmentOffset + offset,
|
||||
value: destinationHash
|
||||
});
|
||||
}
|
||||
offset += 1;
|
||||
// Source Hash (1 byte)
|
||||
const sourceHash = (0, hex_1.byteToHex)(payload[offset]);
|
||||
if (options?.includeSegments) {
|
||||
segments.push({
|
||||
name: 'Source Hash',
|
||||
description: 'First byte of source node public key',
|
||||
startByte: segmentOffset + offset,
|
||||
endByte: segmentOffset + offset,
|
||||
value: sourceHash
|
||||
});
|
||||
}
|
||||
offset += 1;
|
||||
// Cipher MAC (2 bytes)
|
||||
const cipherMac = (0, hex_1.bytesToHex)(payload.subarray(offset, offset + 2));
|
||||
if (options?.includeSegments) {
|
||||
segments.push({
|
||||
name: 'Cipher MAC',
|
||||
description: 'MAC for encrypted data in next field',
|
||||
startByte: segmentOffset + offset,
|
||||
endByte: segmentOffset + offset + 1,
|
||||
value: cipherMac
|
||||
});
|
||||
}
|
||||
offset += 2;
|
||||
// Ciphertext (remaining bytes)
|
||||
const ciphertext = (0, hex_1.bytesToHex)(payload.subarray(offset));
|
||||
if (options?.includeSegments && payload.length > offset) {
|
||||
segments.push({
|
||||
name: 'Ciphertext',
|
||||
description: 'Encrypted message data (timestamp + message text)',
|
||||
startByte: segmentOffset + offset,
|
||||
endByte: segmentOffset + payload.length - 1,
|
||||
value: ciphertext
|
||||
});
|
||||
}
|
||||
const result = {
|
||||
type: enums_1.PayloadType.TextMessage,
|
||||
version: enums_1.PayloadVersion.Version1,
|
||||
isValid: true,
|
||||
destinationHash,
|
||||
sourceHash,
|
||||
cipherMac,
|
||||
ciphertext,
|
||||
ciphertextLength: payload.length - 4
|
||||
};
|
||||
if (options?.includeSegments) {
|
||||
result.segments = segments;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
catch (error) {
|
||||
return {
|
||||
type: enums_1.PayloadType.TextMessage,
|
||||
version: enums_1.PayloadVersion.Version1,
|
||||
isValid: false,
|
||||
errors: [error instanceof Error ? error.message : 'Failed to decode TextMessage payload'],
|
||||
destinationHash: '',
|
||||
sourceHash: '',
|
||||
cipherMac: '',
|
||||
ciphertext: '',
|
||||
ciphertextLength: 0
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
exports.TextMessagePayloadDecoder = TextMessagePayloadDecoder;
|
||||
//# sourceMappingURL=text-message.js.map
|
||||
1
frontend/lib/meshcore-decoder/dist/decoder/payload-decoders/text-message.js.map
vendored
Normal file
1
frontend/lib/meshcore-decoder/dist/decoder/payload-decoders/text-message.js.map
vendored
Normal file
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"text-message.js","sourceRoot":"","sources":["../../../src/decoder/payload-decoders/text-message.ts"],"names":[],"mappings":"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"}
|
||||
12
frontend/lib/meshcore-decoder/dist/decoder/payload-decoders/trace.d.ts
vendored
Normal file
12
frontend/lib/meshcore-decoder/dist/decoder/payload-decoders/trace.d.ts
vendored
Normal file
@@ -0,0 +1,12 @@
|
||||
import { TracePayload } from '../../types/payloads';
|
||||
import { PayloadSegment } from '../../types/packet';
|
||||
export declare class TracePayloadDecoder {
|
||||
static decode(payload: Uint8Array, pathData?: string[] | null, options?: {
|
||||
includeSegments?: boolean;
|
||||
segmentOffset?: number;
|
||||
}): TracePayload & {
|
||||
segments?: PayloadSegment[];
|
||||
} | null;
|
||||
private static readUint32LE;
|
||||
}
|
||||
//# sourceMappingURL=trace.d.ts.map
|
||||
1
frontend/lib/meshcore-decoder/dist/decoder/payload-decoders/trace.d.ts.map
vendored
Normal file
1
frontend/lib/meshcore-decoder/dist/decoder/payload-decoders/trace.d.ts.map
vendored
Normal file
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"trace.d.ts","sourceRoot":"","sources":["../../../src/decoder/payload-decoders/trace.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,YAAY,EAAE,MAAM,sBAAsB,CAAC;AACpD,OAAO,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AAIpD,qBAAa,mBAAmB;IAC9B,MAAM,CAAC,MAAM,CAAC,OAAO,EAAE,UAAU,EAAE,QAAQ,CAAC,EAAE,MAAM,EAAE,GAAG,IAAI,EAAE,OAAO,CAAC,EAAE;QAAE,eAAe,CAAC,EAAE,OAAO,CAAC;QAAC,aAAa,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG,YAAY,GAAG;QAAE,QAAQ,CAAC,EAAE,cAAc,EAAE,CAAA;KAAE,GAAG,IAAI;IAuItL,OAAO,CAAC,MAAM,CAAC,YAAY;CAM5B"}
|
||||
136
frontend/lib/meshcore-decoder/dist/decoder/payload-decoders/trace.js
vendored
Normal file
136
frontend/lib/meshcore-decoder/dist/decoder/payload-decoders/trace.js
vendored
Normal file
@@ -0,0 +1,136 @@
|
||||
"use strict";
|
||||
// Copyright (c) 2025 Michael Hart: https://github.com/michaelhart/meshcore-decoder
|
||||
// MIT License
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.TracePayloadDecoder = void 0;
|
||||
const enums_1 = require("../../types/enums");
|
||||
const hex_1 = require("../../utils/hex");
|
||||
class TracePayloadDecoder {
|
||||
static decode(payload, pathData, options) {
|
||||
try {
|
||||
if (payload.length < 9) {
|
||||
const result = {
|
||||
type: enums_1.PayloadType.Trace,
|
||||
version: enums_1.PayloadVersion.Version1,
|
||||
isValid: false,
|
||||
errors: ['Trace payload too short (need at least tag(4) + auth(4) + flags(1))'],
|
||||
traceTag: '00000000',
|
||||
authCode: 0,
|
||||
flags: 0,
|
||||
pathHashes: []
|
||||
};
|
||||
if (options?.includeSegments) {
|
||||
result.segments = [{
|
||||
name: 'Invalid Trace Data',
|
||||
description: 'Trace payload too short (minimum 9 bytes required)',
|
||||
startByte: options.segmentOffset || 0,
|
||||
endByte: (options.segmentOffset || 0) + payload.length - 1,
|
||||
value: (0, hex_1.bytesToHex)(payload)
|
||||
}];
|
||||
}
|
||||
return result;
|
||||
}
|
||||
let offset = 0;
|
||||
const segments = [];
|
||||
const segmentOffset = options?.segmentOffset || 0;
|
||||
// Trace Tag (4 bytes) - unique identifier
|
||||
const traceTagRaw = this.readUint32LE(payload, offset);
|
||||
const traceTag = (0, hex_1.numberToHex)(traceTagRaw, 8);
|
||||
if (options?.includeSegments) {
|
||||
segments.push({
|
||||
name: 'Trace Tag',
|
||||
description: `Unique identifier for this trace: 0x${traceTagRaw.toString(16).padStart(8, '0')}`,
|
||||
startByte: segmentOffset + offset,
|
||||
endByte: segmentOffset + offset + 3,
|
||||
value: (0, hex_1.bytesToHex)(payload.slice(offset, offset + 4))
|
||||
});
|
||||
}
|
||||
offset += 4;
|
||||
// Auth Code (4 bytes) - authentication/verification code
|
||||
const authCode = this.readUint32LE(payload, offset);
|
||||
if (options?.includeSegments) {
|
||||
segments.push({
|
||||
name: 'Auth Code',
|
||||
description: `Authentication/verification code: ${authCode}`,
|
||||
startByte: segmentOffset + offset,
|
||||
endByte: segmentOffset + offset + 3,
|
||||
value: (0, hex_1.bytesToHex)(payload.slice(offset, offset + 4))
|
||||
});
|
||||
}
|
||||
offset += 4;
|
||||
// Flags (1 byte) - application-defined control flags
|
||||
const flags = payload[offset];
|
||||
if (options?.includeSegments) {
|
||||
segments.push({
|
||||
name: 'Flags',
|
||||
description: `Application-defined control flags: 0x${flags.toString(16).padStart(2, '0')} (${flags.toString(2).padStart(8, '0')}b)`,
|
||||
startByte: segmentOffset + offset,
|
||||
endByte: segmentOffset + offset,
|
||||
value: flags.toString(16).padStart(2, '0').toUpperCase()
|
||||
});
|
||||
}
|
||||
offset += 1;
|
||||
// remaining bytes are path hashes (node hashes in the trace path)
|
||||
const pathHashes = [];
|
||||
const pathHashesStart = offset;
|
||||
while (offset < payload.length) {
|
||||
pathHashes.push((0, hex_1.byteToHex)(payload[offset]));
|
||||
offset++;
|
||||
}
|
||||
if (options?.includeSegments && pathHashes.length > 0) {
|
||||
const pathHashesDisplay = pathHashes.join(' ');
|
||||
segments.push({
|
||||
name: 'Path Hashes',
|
||||
description: `Node hashes in trace path: ${pathHashesDisplay}`,
|
||||
startByte: segmentOffset + pathHashesStart,
|
||||
endByte: segmentOffset + payload.length - 1,
|
||||
value: (0, hex_1.bytesToHex)(payload.slice(pathHashesStart))
|
||||
});
|
||||
}
|
||||
// extract SNR values from path field for TRACE packets
|
||||
let snrValues;
|
||||
if (pathData && pathData.length > 0) {
|
||||
snrValues = pathData.map(hexByte => {
|
||||
const byteValue = parseInt(hexByte, 16);
|
||||
// convert unsigned byte to signed int8 (SNR values are stored as signed int8 * 4)
|
||||
const snrSigned = byteValue > 127 ? byteValue - 256 : byteValue;
|
||||
return snrSigned / 4.0; // convert to dB
|
||||
});
|
||||
}
|
||||
const result = {
|
||||
type: enums_1.PayloadType.Trace,
|
||||
version: enums_1.PayloadVersion.Version1,
|
||||
isValid: true,
|
||||
traceTag,
|
||||
authCode,
|
||||
flags,
|
||||
pathHashes,
|
||||
snrValues
|
||||
};
|
||||
if (options?.includeSegments) {
|
||||
result.segments = segments;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
catch (error) {
|
||||
return {
|
||||
type: enums_1.PayloadType.Trace,
|
||||
version: enums_1.PayloadVersion.Version1,
|
||||
isValid: false,
|
||||
errors: [error instanceof Error ? error.message : 'Failed to decode trace payload'],
|
||||
traceTag: '00000000',
|
||||
authCode: 0,
|
||||
flags: 0,
|
||||
pathHashes: []
|
||||
};
|
||||
}
|
||||
}
|
||||
static readUint32LE(buffer, offset) {
|
||||
return buffer[offset] |
|
||||
(buffer[offset + 1] << 8) |
|
||||
(buffer[offset + 2] << 16) |
|
||||
(buffer[offset + 3] << 24);
|
||||
}
|
||||
}
|
||||
exports.TracePayloadDecoder = TracePayloadDecoder;
|
||||
//# sourceMappingURL=trace.js.map
|
||||
1
frontend/lib/meshcore-decoder/dist/decoder/payload-decoders/trace.js.map
vendored
Normal file
1
frontend/lib/meshcore-decoder/dist/decoder/payload-decoders/trace.js.map
vendored
Normal file
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"trace.js","sourceRoot":"","sources":["../../../src/decoder/payload-decoders/trace.ts"],"names":[],"mappings":"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"}
|
||||
36
frontend/lib/meshcore-decoder/dist/index.d.ts
vendored
Normal file
36
frontend/lib/meshcore-decoder/dist/index.d.ts
vendored
Normal file
@@ -0,0 +1,36 @@
|
||||
export { MeshCorePacketDecoder } from './decoder/packet-decoder';
|
||||
export { MeshCorePacketDecoder as MeshCoreDecoder } from './decoder/packet-decoder';
|
||||
export type { DecodedPacket, PacketStructure, PacketSegment, PayloadSegment, HeaderBreakdown } from './types/packet';
|
||||
export type { BasePayload, AdvertPayload, TracePayload, GroupTextPayload, RequestPayload, TextMessagePayload, AnonRequestPayload, AckPayload, PathPayload, ResponsePayload, ControlPayloadBase, ControlDiscoverReqPayload, ControlDiscoverRespPayload, ControlPayload, PayloadData } from './types/payloads';
|
||||
export type { CryptoKeyStore, DecryptionOptions, DecryptionResult, ValidationResult } from './types/crypto';
|
||||
export { RouteType, PayloadType, PayloadVersion, DeviceRole, AdvertFlags, RequestType, ControlSubType } from './types/enums';
|
||||
export { MeshCoreKeyStore } from './crypto/key-manager';
|
||||
export { ChannelCrypto } from './crypto/channel-crypto';
|
||||
export { Ed25519SignatureVerifier } from './crypto/ed25519-verifier';
|
||||
export { hexToBytes, bytesToHex, byteToHex, numberToHex } from './utils/hex';
|
||||
export { getRouteTypeName, getPayloadTypeName, getPayloadVersionName, getDeviceRoleName, getRequestTypeName, getControlSubTypeName } from './utils/enum-names';
|
||||
export { createAuthToken, verifyAuthToken, parseAuthToken, decodeAuthTokenPayload } from './utils/auth-token';
|
||||
export type { AuthTokenPayload, AuthToken } from './utils/auth-token';
|
||||
import * as AuthTokenUtils from './utils/auth-token';
|
||||
import { derivePublicKey, validateKeyPair, sign, verify } from './crypto/orlp-ed25519-wasm';
|
||||
export declare const Utils: {
|
||||
derivePublicKey: typeof derivePublicKey;
|
||||
validateKeyPair: typeof validateKeyPair;
|
||||
sign: typeof sign;
|
||||
verify: typeof verify;
|
||||
createAuthToken(payload: AuthTokenUtils.AuthTokenPayload, privateKeyHex: string, publicKeyHex: string): Promise<string>;
|
||||
verifyAuthToken(token: string, expectedPublicKeyHex?: string): Promise<AuthTokenUtils.AuthTokenPayload | null>;
|
||||
parseAuthToken(token: string): AuthTokenUtils.AuthToken | null;
|
||||
decodeAuthTokenPayload(token: string): AuthTokenUtils.AuthTokenPayload | null;
|
||||
byteToHex(byte: number): string;
|
||||
bytesToHex(bytes: Uint8Array): string;
|
||||
numberToHex(num: number, padLength?: number): string;
|
||||
hexToBytes(hex: string): Uint8Array;
|
||||
getRouteTypeName(routeType: import("./types/enums").RouteType): string;
|
||||
getPayloadTypeName(payloadType: import("./types/enums").PayloadType): string;
|
||||
getPayloadVersionName(version: import("./types/enums").PayloadVersion): string;
|
||||
getDeviceRoleName(role: import("./types/enums").DeviceRole): string;
|
||||
getRequestTypeName(requestType: import("./types/enums").RequestType): string;
|
||||
getControlSubTypeName(subType: import("./types/enums").ControlSubType): string;
|
||||
};
|
||||
//# sourceMappingURL=index.d.ts.map
|
||||
1
frontend/lib/meshcore-decoder/dist/index.d.ts.map
vendored
Normal file
1
frontend/lib/meshcore-decoder/dist/index.d.ts.map
vendored
Normal file
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,qBAAqB,EAAE,MAAM,0BAA0B,CAAC;AACjE,OAAO,EAAE,qBAAqB,IAAI,eAAe,EAAE,MAAM,0BAA0B,CAAC;AAGpF,YAAY,EAAE,aAAa,EAAE,eAAe,EAAE,aAAa,EAAE,cAAc,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAC;AACrH,YAAY,EACV,WAAW,EACX,aAAa,EACb,YAAY,EACZ,gBAAgB,EAChB,cAAc,EACd,kBAAkB,EAClB,kBAAkB,EAClB,UAAU,EACV,WAAW,EACX,eAAe,EACf,kBAAkB,EAClB,yBAAyB,EACzB,0BAA0B,EAC1B,cAAc,EACd,WAAW,EACZ,MAAM,kBAAkB,CAAC;AAC1B,YAAY,EAAE,cAAc,EAAE,iBAAiB,EAAE,gBAAgB,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAC;AAG5G,OAAO,EACL,SAAS,EACT,WAAW,EACX,cAAc,EACd,UAAU,EACV,WAAW,EACX,WAAW,EACX,cAAc,EACf,MAAM,eAAe,CAAC;AAGvB,OAAO,EAAE,gBAAgB,EAAE,MAAM,sBAAsB,CAAC;AACxD,OAAO,EAAE,aAAa,EAAE,MAAM,yBAAyB,CAAC;AACxD,OAAO,EAAE,wBAAwB,EAAE,MAAM,2BAA2B,CAAC;AAGrE,OAAO,EAAE,UAAU,EAAE,UAAU,EAAE,SAAS,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAC7E,OAAO,EACL,gBAAgB,EAChB,kBAAkB,EAClB,qBAAqB,EACrB,iBAAiB,EACjB,kBAAkB,EAClB,qBAAqB,EACtB,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EACL,eAAe,EACf,eAAe,EACf,cAAc,EACd,sBAAsB,EACvB,MAAM,oBAAoB,CAAC;AAC5B,YAAY,EAAE,gBAAgB,EAAE,SAAS,EAAE,MAAM,oBAAoB,CAAC;AAItE,OAAO,KAAK,cAAc,MAAM,oBAAoB,CAAC;AACrD,OAAO,EAAE,eAAe,EAAE,eAAe,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,4BAA4B,CAAC;AAE5F,eAAO,MAAM,KAAK;;;;;;;;;;;;;;;;;;;CAQjB,CAAC"}
|
||||
91
frontend/lib/meshcore-decoder/dist/index.js
vendored
Normal file
91
frontend/lib/meshcore-decoder/dist/index.js
vendored
Normal file
@@ -0,0 +1,91 @@
|
||||
"use strict";
|
||||
// MeshCore Packet Decoder
|
||||
// Copyright (c) 2025 Michael Hart: https://github.com/michaelhart/meshcore-decoder
|
||||
// MIT License
|
||||
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
||||
if (k2 === undefined) k2 = k;
|
||||
var desc = Object.getOwnPropertyDescriptor(m, k);
|
||||
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
||||
desc = { enumerable: true, get: function() { return m[k]; } };
|
||||
}
|
||||
Object.defineProperty(o, k2, desc);
|
||||
}) : (function(o, m, k, k2) {
|
||||
if (k2 === undefined) k2 = k;
|
||||
o[k2] = m[k];
|
||||
}));
|
||||
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
||||
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
||||
}) : function(o, v) {
|
||||
o["default"] = v;
|
||||
});
|
||||
var __importStar = (this && this.__importStar) || (function () {
|
||||
var ownKeys = function(o) {
|
||||
ownKeys = Object.getOwnPropertyNames || function (o) {
|
||||
var ar = [];
|
||||
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
||||
return ar;
|
||||
};
|
||||
return ownKeys(o);
|
||||
};
|
||||
return function (mod) {
|
||||
if (mod && mod.__esModule) return mod;
|
||||
var result = {};
|
||||
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
||||
__setModuleDefault(result, mod);
|
||||
return result;
|
||||
};
|
||||
})();
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.Utils = exports.decodeAuthTokenPayload = exports.parseAuthToken = exports.verifyAuthToken = exports.createAuthToken = exports.getControlSubTypeName = exports.getRequestTypeName = exports.getDeviceRoleName = exports.getPayloadVersionName = exports.getPayloadTypeName = exports.getRouteTypeName = exports.numberToHex = exports.byteToHex = exports.bytesToHex = exports.hexToBytes = exports.Ed25519SignatureVerifier = exports.ChannelCrypto = exports.MeshCoreKeyStore = exports.ControlSubType = exports.RequestType = exports.AdvertFlags = exports.DeviceRole = exports.PayloadVersion = exports.PayloadType = exports.RouteType = exports.MeshCoreDecoder = exports.MeshCorePacketDecoder = void 0;
|
||||
var packet_decoder_1 = require("./decoder/packet-decoder");
|
||||
Object.defineProperty(exports, "MeshCorePacketDecoder", { enumerable: true, get: function () { return packet_decoder_1.MeshCorePacketDecoder; } });
|
||||
var packet_decoder_2 = require("./decoder/packet-decoder");
|
||||
Object.defineProperty(exports, "MeshCoreDecoder", { enumerable: true, get: function () { return packet_decoder_2.MeshCorePacketDecoder; } });
|
||||
// Enum exports
|
||||
var enums_1 = require("./types/enums");
|
||||
Object.defineProperty(exports, "RouteType", { enumerable: true, get: function () { return enums_1.RouteType; } });
|
||||
Object.defineProperty(exports, "PayloadType", { enumerable: true, get: function () { return enums_1.PayloadType; } });
|
||||
Object.defineProperty(exports, "PayloadVersion", { enumerable: true, get: function () { return enums_1.PayloadVersion; } });
|
||||
Object.defineProperty(exports, "DeviceRole", { enumerable: true, get: function () { return enums_1.DeviceRole; } });
|
||||
Object.defineProperty(exports, "AdvertFlags", { enumerable: true, get: function () { return enums_1.AdvertFlags; } });
|
||||
Object.defineProperty(exports, "RequestType", { enumerable: true, get: function () { return enums_1.RequestType; } });
|
||||
Object.defineProperty(exports, "ControlSubType", { enumerable: true, get: function () { return enums_1.ControlSubType; } });
|
||||
// Crypto exports
|
||||
var key_manager_1 = require("./crypto/key-manager");
|
||||
Object.defineProperty(exports, "MeshCoreKeyStore", { enumerable: true, get: function () { return key_manager_1.MeshCoreKeyStore; } });
|
||||
var channel_crypto_1 = require("./crypto/channel-crypto");
|
||||
Object.defineProperty(exports, "ChannelCrypto", { enumerable: true, get: function () { return channel_crypto_1.ChannelCrypto; } });
|
||||
var ed25519_verifier_1 = require("./crypto/ed25519-verifier");
|
||||
Object.defineProperty(exports, "Ed25519SignatureVerifier", { enumerable: true, get: function () { return ed25519_verifier_1.Ed25519SignatureVerifier; } });
|
||||
// Utility exports
|
||||
var hex_1 = require("./utils/hex");
|
||||
Object.defineProperty(exports, "hexToBytes", { enumerable: true, get: function () { return hex_1.hexToBytes; } });
|
||||
Object.defineProperty(exports, "bytesToHex", { enumerable: true, get: function () { return hex_1.bytesToHex; } });
|
||||
Object.defineProperty(exports, "byteToHex", { enumerable: true, get: function () { return hex_1.byteToHex; } });
|
||||
Object.defineProperty(exports, "numberToHex", { enumerable: true, get: function () { return hex_1.numberToHex; } });
|
||||
var enum_names_1 = require("./utils/enum-names");
|
||||
Object.defineProperty(exports, "getRouteTypeName", { enumerable: true, get: function () { return enum_names_1.getRouteTypeName; } });
|
||||
Object.defineProperty(exports, "getPayloadTypeName", { enumerable: true, get: function () { return enum_names_1.getPayloadTypeName; } });
|
||||
Object.defineProperty(exports, "getPayloadVersionName", { enumerable: true, get: function () { return enum_names_1.getPayloadVersionName; } });
|
||||
Object.defineProperty(exports, "getDeviceRoleName", { enumerable: true, get: function () { return enum_names_1.getDeviceRoleName; } });
|
||||
Object.defineProperty(exports, "getRequestTypeName", { enumerable: true, get: function () { return enum_names_1.getRequestTypeName; } });
|
||||
Object.defineProperty(exports, "getControlSubTypeName", { enumerable: true, get: function () { return enum_names_1.getControlSubTypeName; } });
|
||||
var auth_token_1 = require("./utils/auth-token");
|
||||
Object.defineProperty(exports, "createAuthToken", { enumerable: true, get: function () { return auth_token_1.createAuthToken; } });
|
||||
Object.defineProperty(exports, "verifyAuthToken", { enumerable: true, get: function () { return auth_token_1.verifyAuthToken; } });
|
||||
Object.defineProperty(exports, "parseAuthToken", { enumerable: true, get: function () { return auth_token_1.parseAuthToken; } });
|
||||
Object.defineProperty(exports, "decodeAuthTokenPayload", { enumerable: true, get: function () { return auth_token_1.decodeAuthTokenPayload; } });
|
||||
const EnumUtils = __importStar(require("./utils/enum-names"));
|
||||
const HexUtils = __importStar(require("./utils/hex"));
|
||||
const AuthTokenUtils = __importStar(require("./utils/auth-token"));
|
||||
const orlp_ed25519_wasm_1 = require("./crypto/orlp-ed25519-wasm");
|
||||
exports.Utils = {
|
||||
...EnumUtils,
|
||||
...HexUtils,
|
||||
...AuthTokenUtils,
|
||||
derivePublicKey: orlp_ed25519_wasm_1.derivePublicKey,
|
||||
validateKeyPair: orlp_ed25519_wasm_1.validateKeyPair,
|
||||
sign: orlp_ed25519_wasm_1.sign,
|
||||
verify: orlp_ed25519_wasm_1.verify
|
||||
};
|
||||
//# sourceMappingURL=index.js.map
|
||||
1
frontend/lib/meshcore-decoder/dist/index.js.map
vendored
Normal file
1
frontend/lib/meshcore-decoder/dist/index.js.map
vendored
Normal file
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AAAA,0BAA0B;AAC1B,mFAAmF;AACnF,cAAc;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAEd,2DAAiE;AAAxD,uHAAA,qBAAqB,OAAA;AAC9B,2DAAoF;AAA3E,iHAAA,qBAAqB,OAAmB;AAuBjD,eAAe;AACf,uCAQuB;AAPrB,kGAAA,SAAS,OAAA;AACT,oGAAA,WAAW,OAAA;AACX,uGAAA,cAAc,OAAA;AACd,mGAAA,UAAU,OAAA;AACV,oGAAA,WAAW,OAAA;AACX,oGAAA,WAAW,OAAA;AACX,uGAAA,cAAc,OAAA;AAGhB,iBAAiB;AACjB,oDAAwD;AAA/C,+GAAA,gBAAgB,OAAA;AACzB,0DAAwD;AAA/C,+GAAA,aAAa,OAAA;AACtB,8DAAqE;AAA5D,4HAAA,wBAAwB,OAAA;AAEjC,kBAAkB;AAClB,mCAA6E;AAApE,iGAAA,UAAU,OAAA;AAAE,iGAAA,UAAU,OAAA;AAAE,gGAAA,SAAS,OAAA;AAAE,kGAAA,WAAW,OAAA;AACvD,iDAO4B;AAN1B,8GAAA,gBAAgB,OAAA;AAChB,gHAAA,kBAAkB,OAAA;AAClB,mHAAA,qBAAqB,OAAA;AACrB,+GAAA,iBAAiB,OAAA;AACjB,gHAAA,kBAAkB,OAAA;AAClB,mHAAA,qBAAqB,OAAA;AAEvB,iDAK4B;AAJ1B,6GAAA,eAAe,OAAA;AACf,6GAAA,eAAe,OAAA;AACf,4GAAA,cAAc,OAAA;AACd,oHAAA,sBAAsB,OAAA;AAIxB,8DAAgD;AAChD,sDAAwC;AACxC,mEAAqD;AACrD,kEAA4F;AAE/E,QAAA,KAAK,GAAG;IACnB,GAAG,SAAS;IACZ,GAAG,QAAQ;IACX,GAAG,cAAc;IACjB,eAAe,EAAf,mCAAe;IACf,eAAe,EAAf,mCAAe;IACf,IAAI,EAAJ,wBAAI;IACJ,MAAM,EAAN,0BAAM;CACP,CAAC"}
|
||||
22
frontend/lib/meshcore-decoder/dist/types/crypto.d.ts
vendored
Normal file
22
frontend/lib/meshcore-decoder/dist/types/crypto.d.ts
vendored
Normal file
@@ -0,0 +1,22 @@
|
||||
export interface CryptoKeyStore {
|
||||
nodeKeys: Map<string, string>;
|
||||
addNodeKey(publicKey: string, privateKey: string): void;
|
||||
hasChannelKey(channelHash: string): boolean;
|
||||
hasNodeKey(publicKey: string): boolean;
|
||||
getChannelKeys(channelHash: string): string[];
|
||||
}
|
||||
export interface DecryptionOptions {
|
||||
keyStore?: CryptoKeyStore;
|
||||
attemptDecryption?: boolean;
|
||||
includeRawCiphertext?: boolean;
|
||||
}
|
||||
export interface DecryptionResult {
|
||||
success: boolean;
|
||||
data?: any;
|
||||
error?: string;
|
||||
}
|
||||
export interface ValidationResult {
|
||||
isValid: boolean;
|
||||
errors?: string[];
|
||||
}
|
||||
//# sourceMappingURL=crypto.d.ts.map
|
||||
1
frontend/lib/meshcore-decoder/dist/types/crypto.d.ts.map
vendored
Normal file
1
frontend/lib/meshcore-decoder/dist/types/crypto.d.ts.map
vendored
Normal file
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"crypto.d.ts","sourceRoot":"","sources":["../../src/types/crypto.ts"],"names":[],"mappings":"AAGA,MAAM,WAAW,cAAc;IAE7B,QAAQ,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAG9B,UAAU,CAAC,SAAS,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAGxD,aAAa,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC;IAC5C,UAAU,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC;IACvC,cAAc,CAAC,WAAW,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC;CAC/C;AAED,MAAM,WAAW,iBAAiB;IAChC,QAAQ,CAAC,EAAE,cAAc,CAAC;IAC1B,iBAAiB,CAAC,EAAE,OAAO,CAAC;IAC5B,oBAAoB,CAAC,EAAE,OAAO,CAAC;CAChC;AAED,MAAM,WAAW,gBAAgB;IAC/B,OAAO,EAAE,OAAO,CAAC;IACjB,IAAI,CAAC,EAAE,GAAG,CAAC;IACX,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,gBAAgB;IAC/B,OAAO,EAAE,OAAO,CAAC;IACjB,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC;CACnB"}
|
||||
5
frontend/lib/meshcore-decoder/dist/types/crypto.js
vendored
Normal file
5
frontend/lib/meshcore-decoder/dist/types/crypto.js
vendored
Normal file
@@ -0,0 +1,5 @@
|
||||
"use strict";
|
||||
// Copyright (c) 2025 Michael Hart: https://github.com/michaelhart/meshcore-decoder
|
||||
// MIT License
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
//# sourceMappingURL=crypto.js.map
|
||||
1
frontend/lib/meshcore-decoder/dist/types/crypto.js.map
vendored
Normal file
1
frontend/lib/meshcore-decoder/dist/types/crypto.js.map
vendored
Normal file
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"crypto.js","sourceRoot":"","sources":["../../src/types/crypto.ts"],"names":[],"mappings":";AAAA,mFAAmF;AACnF,cAAc"}
|
||||
52
frontend/lib/meshcore-decoder/dist/types/enums.d.ts
vendored
Normal file
52
frontend/lib/meshcore-decoder/dist/types/enums.d.ts
vendored
Normal file
@@ -0,0 +1,52 @@
|
||||
export declare enum RouteType {
|
||||
TransportFlood = 0,
|
||||
Flood = 1,
|
||||
Direct = 2,
|
||||
TransportDirect = 3
|
||||
}
|
||||
export declare enum PayloadType {
|
||||
Request = 0,
|
||||
Response = 1,
|
||||
TextMessage = 2,
|
||||
Ack = 3,
|
||||
Advert = 4,
|
||||
GroupText = 5,
|
||||
GroupData = 6,
|
||||
AnonRequest = 7,
|
||||
Path = 8,
|
||||
Trace = 9,
|
||||
Multipart = 10,
|
||||
Control = 11,
|
||||
RawCustom = 15
|
||||
}
|
||||
export declare enum ControlSubType {
|
||||
NodeDiscoverReq = 128,
|
||||
NodeDiscoverResp = 144
|
||||
}
|
||||
export declare enum PayloadVersion {
|
||||
Version1 = 0,
|
||||
Version2 = 1,
|
||||
Version3 = 2,
|
||||
Version4 = 3
|
||||
}
|
||||
export declare enum DeviceRole {
|
||||
Unknown = 0,
|
||||
ChatNode = 1,
|
||||
Repeater = 2,
|
||||
RoomServer = 3,
|
||||
Sensor = 4
|
||||
}
|
||||
export declare enum AdvertFlags {
|
||||
HasLocation = 16,
|
||||
HasFeature1 = 32,
|
||||
HasFeature2 = 64,
|
||||
HasName = 128
|
||||
}
|
||||
export declare enum RequestType {
|
||||
GetStats = 1,
|
||||
Keepalive = 2,// deprecated
|
||||
GetTelemetryData = 3,
|
||||
GetMinMaxAvgData = 4,
|
||||
GetAccessList = 5
|
||||
}
|
||||
//# sourceMappingURL=enums.d.ts.map
|
||||
1
frontend/lib/meshcore-decoder/dist/types/enums.d.ts.map
vendored
Normal file
1
frontend/lib/meshcore-decoder/dist/types/enums.d.ts.map
vendored
Normal file
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"enums.d.ts","sourceRoot":"","sources":["../../src/types/enums.ts"],"names":[],"mappings":"AAEA,oBAAY,SAAS;IACnB,cAAc,IAAO;IACrB,KAAK,IAAO;IACZ,MAAM,IAAO;IACb,eAAe,IAAO;CACvB;AAED,oBAAY,WAAW;IACrB,OAAO,IAAO;IACd,QAAQ,IAAO;IACf,WAAW,IAAO;IAClB,GAAG,IAAO;IACV,MAAM,IAAO;IACb,SAAS,IAAO;IAChB,SAAS,IAAO;IAChB,WAAW,IAAO;IAClB,IAAI,IAAO;IACX,KAAK,IAAO;IACZ,SAAS,KAAO;IAChB,OAAO,KAAO;IACd,SAAS,KAAO;CACjB;AAGD,oBAAY,cAAc;IACxB,eAAe,MAAO;IACtB,gBAAgB,MAAO;CACxB;AAED,oBAAY,cAAc;IACxB,QAAQ,IAAO;IACf,QAAQ,IAAO;IACf,QAAQ,IAAO;IACf,QAAQ,IAAO;CAChB;AAED,oBAAY,UAAU;IACpB,OAAO,IAAO;IACd,QAAQ,IAAO;IACf,QAAQ,IAAO;IACf,UAAU,IAAO;IACjB,MAAM,IAAO;CACd;AAED,oBAAY,WAAW;IACrB,WAAW,KAAO;IAClB,WAAW,KAAO;IAClB,WAAW,KAAO;IAClB,OAAO,MAAO;CACf;AAED,oBAAY,WAAW;IACrB,QAAQ,IAAO;IACf,SAAS,IAAO,CAAE,aAAa;IAC/B,gBAAgB,IAAO;IACvB,gBAAgB,IAAO;IACvB,aAAa,IAAO;CACrB"}
|
||||
64
frontend/lib/meshcore-decoder/dist/types/enums.js
vendored
Normal file
64
frontend/lib/meshcore-decoder/dist/types/enums.js
vendored
Normal file
@@ -0,0 +1,64 @@
|
||||
"use strict";
|
||||
// Reference: https://github.com/meshcore-dev/MeshCore/blob/main/docs/packet_structure.md
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.RequestType = exports.AdvertFlags = exports.DeviceRole = exports.PayloadVersion = exports.ControlSubType = exports.PayloadType = exports.RouteType = void 0;
|
||||
var RouteType;
|
||||
(function (RouteType) {
|
||||
RouteType[RouteType["TransportFlood"] = 0] = "TransportFlood";
|
||||
RouteType[RouteType["Flood"] = 1] = "Flood";
|
||||
RouteType[RouteType["Direct"] = 2] = "Direct";
|
||||
RouteType[RouteType["TransportDirect"] = 3] = "TransportDirect";
|
||||
})(RouteType || (exports.RouteType = RouteType = {}));
|
||||
var PayloadType;
|
||||
(function (PayloadType) {
|
||||
PayloadType[PayloadType["Request"] = 0] = "Request";
|
||||
PayloadType[PayloadType["Response"] = 1] = "Response";
|
||||
PayloadType[PayloadType["TextMessage"] = 2] = "TextMessage";
|
||||
PayloadType[PayloadType["Ack"] = 3] = "Ack";
|
||||
PayloadType[PayloadType["Advert"] = 4] = "Advert";
|
||||
PayloadType[PayloadType["GroupText"] = 5] = "GroupText";
|
||||
PayloadType[PayloadType["GroupData"] = 6] = "GroupData";
|
||||
PayloadType[PayloadType["AnonRequest"] = 7] = "AnonRequest";
|
||||
PayloadType[PayloadType["Path"] = 8] = "Path";
|
||||
PayloadType[PayloadType["Trace"] = 9] = "Trace";
|
||||
PayloadType[PayloadType["Multipart"] = 10] = "Multipart";
|
||||
PayloadType[PayloadType["Control"] = 11] = "Control";
|
||||
PayloadType[PayloadType["RawCustom"] = 15] = "RawCustom";
|
||||
})(PayloadType || (exports.PayloadType = PayloadType = {}));
|
||||
// Control packet sub-types (upper 4 bits of first payload byte)
|
||||
var ControlSubType;
|
||||
(function (ControlSubType) {
|
||||
ControlSubType[ControlSubType["NodeDiscoverReq"] = 128] = "NodeDiscoverReq";
|
||||
ControlSubType[ControlSubType["NodeDiscoverResp"] = 144] = "NodeDiscoverResp";
|
||||
})(ControlSubType || (exports.ControlSubType = ControlSubType = {}));
|
||||
var PayloadVersion;
|
||||
(function (PayloadVersion) {
|
||||
PayloadVersion[PayloadVersion["Version1"] = 0] = "Version1";
|
||||
PayloadVersion[PayloadVersion["Version2"] = 1] = "Version2";
|
||||
PayloadVersion[PayloadVersion["Version3"] = 2] = "Version3";
|
||||
PayloadVersion[PayloadVersion["Version4"] = 3] = "Version4";
|
||||
})(PayloadVersion || (exports.PayloadVersion = PayloadVersion = {}));
|
||||
var DeviceRole;
|
||||
(function (DeviceRole) {
|
||||
DeviceRole[DeviceRole["Unknown"] = 0] = "Unknown";
|
||||
DeviceRole[DeviceRole["ChatNode"] = 1] = "ChatNode";
|
||||
DeviceRole[DeviceRole["Repeater"] = 2] = "Repeater";
|
||||
DeviceRole[DeviceRole["RoomServer"] = 3] = "RoomServer";
|
||||
DeviceRole[DeviceRole["Sensor"] = 4] = "Sensor";
|
||||
})(DeviceRole || (exports.DeviceRole = DeviceRole = {}));
|
||||
var AdvertFlags;
|
||||
(function (AdvertFlags) {
|
||||
AdvertFlags[AdvertFlags["HasLocation"] = 16] = "HasLocation";
|
||||
AdvertFlags[AdvertFlags["HasFeature1"] = 32] = "HasFeature1";
|
||||
AdvertFlags[AdvertFlags["HasFeature2"] = 64] = "HasFeature2";
|
||||
AdvertFlags[AdvertFlags["HasName"] = 128] = "HasName";
|
||||
})(AdvertFlags || (exports.AdvertFlags = AdvertFlags = {}));
|
||||
var RequestType;
|
||||
(function (RequestType) {
|
||||
RequestType[RequestType["GetStats"] = 1] = "GetStats";
|
||||
RequestType[RequestType["Keepalive"] = 2] = "Keepalive";
|
||||
RequestType[RequestType["GetTelemetryData"] = 3] = "GetTelemetryData";
|
||||
RequestType[RequestType["GetMinMaxAvgData"] = 4] = "GetMinMaxAvgData";
|
||||
RequestType[RequestType["GetAccessList"] = 5] = "GetAccessList";
|
||||
})(RequestType || (exports.RequestType = RequestType = {}));
|
||||
//# sourceMappingURL=enums.js.map
|
||||
1
frontend/lib/meshcore-decoder/dist/types/enums.js.map
vendored
Normal file
1
frontend/lib/meshcore-decoder/dist/types/enums.js.map
vendored
Normal file
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"enums.js","sourceRoot":"","sources":["../../src/types/enums.ts"],"names":[],"mappings":";AAAA,yFAAyF;;;AAEzF,IAAY,SAKX;AALD,WAAY,SAAS;IACnB,6DAAqB,CAAA;IACrB,2CAAY,CAAA;IACZ,6CAAa,CAAA;IACb,+DAAsB,CAAA;AACxB,CAAC,EALW,SAAS,yBAAT,SAAS,QAKpB;AAED,IAAY,WAcX;AAdD,WAAY,WAAW;IACrB,mDAAc,CAAA;IACd,qDAAe,CAAA;IACf,2DAAkB,CAAA;IAClB,2CAAU,CAAA;IACV,iDAAa,CAAA;IACb,uDAAgB,CAAA;IAChB,uDAAgB,CAAA;IAChB,2DAAkB,CAAA;IAClB,6CAAW,CAAA;IACX,+CAAY,CAAA;IACZ,wDAAgB,CAAA;IAChB,oDAAc,CAAA;IACd,wDAAgB,CAAA;AAClB,CAAC,EAdW,WAAW,2BAAX,WAAW,QActB;AAED,gEAAgE;AAChE,IAAY,cAGX;AAHD,WAAY,cAAc;IACxB,2EAAsB,CAAA;IACtB,6EAAuB,CAAA;AACzB,CAAC,EAHW,cAAc,8BAAd,cAAc,QAGzB;AAED,IAAY,cAKX;AALD,WAAY,cAAc;IACxB,2DAAe,CAAA;IACf,2DAAe,CAAA;IACf,2DAAe,CAAA;IACf,2DAAe,CAAA;AACjB,CAAC,EALW,cAAc,8BAAd,cAAc,QAKzB;AAED,IAAY,UAMX;AAND,WAAY,UAAU;IACpB,iDAAc,CAAA;IACd,mDAAe,CAAA;IACf,mDAAe,CAAA;IACf,uDAAiB,CAAA;IACjB,+CAAa,CAAA;AACf,CAAC,EANW,UAAU,0BAAV,UAAU,QAMrB;AAED,IAAY,WAKX;AALD,WAAY,WAAW;IACrB,4DAAkB,CAAA;IAClB,4DAAkB,CAAA;IAClB,4DAAkB,CAAA;IAClB,qDAAc,CAAA;AAChB,CAAC,EALW,WAAW,2BAAX,WAAW,QAKtB;AAED,IAAY,WAMX;AAND,WAAY,WAAW;IACrB,qDAAe,CAAA;IACf,uDAAgB,CAAA;IAChB,qEAAuB,CAAA;IACvB,qEAAuB,CAAA;IACvB,+DAAoB,CAAA;AACtB,CAAC,EANW,WAAW,2BAAX,WAAW,QAMtB"}
|
||||
57
frontend/lib/meshcore-decoder/dist/types/packet.d.ts
vendored
Normal file
57
frontend/lib/meshcore-decoder/dist/types/packet.d.ts
vendored
Normal file
@@ -0,0 +1,57 @@
|
||||
import { RouteType, PayloadType, PayloadVersion } from './enums';
|
||||
import { PayloadData } from './payloads';
|
||||
export interface DecodedPacket {
|
||||
messageHash: string;
|
||||
routeType: RouteType;
|
||||
payloadType: PayloadType;
|
||||
payloadVersion: PayloadVersion;
|
||||
transportCodes?: [number, number];
|
||||
pathLength: number;
|
||||
pathHashSize?: number;
|
||||
path: string[] | null;
|
||||
payload: {
|
||||
raw: string;
|
||||
decoded: PayloadData | null;
|
||||
};
|
||||
totalBytes: number;
|
||||
isValid: boolean;
|
||||
errors?: string[];
|
||||
}
|
||||
export interface PacketStructure {
|
||||
segments: PacketSegment[];
|
||||
totalBytes: number;
|
||||
rawHex: string;
|
||||
messageHash: string;
|
||||
payload: {
|
||||
segments: PayloadSegment[];
|
||||
hex: string;
|
||||
startByte: number;
|
||||
type: string;
|
||||
};
|
||||
}
|
||||
export interface PacketSegment {
|
||||
name: string;
|
||||
description: string;
|
||||
startByte: number;
|
||||
endByte: number;
|
||||
value: string;
|
||||
headerBreakdown?: HeaderBreakdown;
|
||||
}
|
||||
export interface PayloadSegment {
|
||||
name: string;
|
||||
description: string;
|
||||
startByte: number;
|
||||
endByte: number;
|
||||
value: string;
|
||||
decryptedMessage?: string;
|
||||
}
|
||||
export interface HeaderBreakdown {
|
||||
fullBinary: string;
|
||||
fields: Array<{
|
||||
bits: string;
|
||||
field: string;
|
||||
value: string;
|
||||
binary: string;
|
||||
}>;
|
||||
}
|
||||
//# sourceMappingURL=packet.d.ts.map
|
||||
1
frontend/lib/meshcore-decoder/dist/types/packet.d.ts.map
vendored
Normal file
1
frontend/lib/meshcore-decoder/dist/types/packet.d.ts.map
vendored
Normal file
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"packet.d.ts","sourceRoot":"","sources":["../../src/types/packet.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,WAAW,EAAE,cAAc,EAAE,MAAM,SAAS,CAAC;AACjE,OAAO,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAGzC,MAAM,WAAW,aAAa;IAE5B,WAAW,EAAE,MAAM,CAAC;IAGpB,SAAS,EAAE,SAAS,CAAC;IACrB,WAAW,EAAE,WAAW,CAAC;IACzB,cAAc,EAAE,cAAc,CAAC;IAG/B,cAAc,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAClC,UAAU,EAAE,MAAM,CAAC;IACnB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,IAAI,EAAE,MAAM,EAAE,GAAG,IAAI,CAAC;IAGtB,OAAO,EAAE;QACP,GAAG,EAAE,MAAM,CAAC;QACZ,OAAO,EAAE,WAAW,GAAG,IAAI,CAAC;KAC7B,CAAC;IAGF,UAAU,EAAE,MAAM,CAAC;IACnB,OAAO,EAAE,OAAO,CAAC;IACjB,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC;CACnB;AAGD,MAAM,WAAW,eAAe;IAC9B,QAAQ,EAAE,aAAa,EAAE,CAAC;IAC1B,UAAU,EAAE,MAAM,CAAC;IACnB,MAAM,EAAE,MAAM,CAAC;IACf,WAAW,EAAE,MAAM,CAAC;IACpB,OAAO,EAAE;QACP,QAAQ,EAAE,cAAc,EAAE,CAAC;QAC3B,GAAG,EAAE,MAAM,CAAC;QACZ,SAAS,EAAE,MAAM,CAAC;QAClB,IAAI,EAAE,MAAM,CAAC;KACd,CAAC;CACH;AAED,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,MAAM,CAAC;IACd,eAAe,CAAC,EAAE,eAAe,CAAC;CACnC;AAED,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,MAAM,CAAC;IACd,gBAAgB,CAAC,EAAE,MAAM,CAAC;CAC3B;AAED,MAAM,WAAW,eAAe;IAC9B,UAAU,EAAE,MAAM,CAAC;IACnB,MAAM,EAAE,KAAK,CAAC;QACZ,IAAI,EAAE,MAAM,CAAC;QACb,KAAK,EAAE,MAAM,CAAC;QACd,KAAK,EAAE,MAAM,CAAC;QACd,MAAM,EAAE,MAAM,CAAC;KAChB,CAAC,CAAC;CACJ"}
|
||||
3
frontend/lib/meshcore-decoder/dist/types/packet.js
vendored
Normal file
3
frontend/lib/meshcore-decoder/dist/types/packet.js
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
//# sourceMappingURL=packet.js.map
|
||||
1
frontend/lib/meshcore-decoder/dist/types/packet.js.map
vendored
Normal file
1
frontend/lib/meshcore-decoder/dist/types/packet.js.map
vendored
Normal file
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"packet.js","sourceRoot":"","sources":["../../src/types/packet.ts"],"names":[],"mappings":""}
|
||||
128
frontend/lib/meshcore-decoder/dist/types/payloads.d.ts
vendored
Normal file
128
frontend/lib/meshcore-decoder/dist/types/payloads.d.ts
vendored
Normal file
@@ -0,0 +1,128 @@
|
||||
import { PayloadType, PayloadVersion, DeviceRole, RequestType, ControlSubType } from './enums';
|
||||
export interface BasePayload {
|
||||
type: PayloadType;
|
||||
version: PayloadVersion;
|
||||
isValid: boolean;
|
||||
errors?: string[];
|
||||
}
|
||||
export interface AdvertPayload extends BasePayload {
|
||||
publicKey: string;
|
||||
timestamp: number;
|
||||
signature: string;
|
||||
signatureValid?: boolean;
|
||||
signatureError?: string;
|
||||
appData: {
|
||||
flags: number;
|
||||
deviceRole: DeviceRole;
|
||||
hasLocation: boolean;
|
||||
hasName: boolean;
|
||||
location?: {
|
||||
latitude: number;
|
||||
longitude: number;
|
||||
};
|
||||
name?: string;
|
||||
};
|
||||
}
|
||||
export interface TracePayload extends BasePayload {
|
||||
traceTag: string;
|
||||
authCode: number;
|
||||
flags: number;
|
||||
pathHashes: string[];
|
||||
snrValues?: number[];
|
||||
}
|
||||
export interface GroupTextPayload extends BasePayload {
|
||||
channelHash: string;
|
||||
cipherMac: string;
|
||||
ciphertext: string;
|
||||
ciphertextLength: number;
|
||||
decrypted?: {
|
||||
timestamp: number;
|
||||
flags: number;
|
||||
sender?: string;
|
||||
message: string;
|
||||
};
|
||||
}
|
||||
export interface RequestPayload extends BasePayload {
|
||||
destinationHash: string;
|
||||
sourceHash: string;
|
||||
cipherMac: string;
|
||||
ciphertext: string;
|
||||
timestamp: number;
|
||||
requestType: RequestType;
|
||||
requestData?: string;
|
||||
decrypted?: {
|
||||
timestamp: number;
|
||||
requestType: RequestType;
|
||||
requestData?: string;
|
||||
};
|
||||
}
|
||||
export interface TextMessagePayload extends BasePayload {
|
||||
destinationHash: string;
|
||||
sourceHash: string;
|
||||
cipherMac: string;
|
||||
ciphertext: string;
|
||||
ciphertextLength: number;
|
||||
decrypted?: {
|
||||
timestamp: number;
|
||||
flags: number;
|
||||
attempt: number;
|
||||
message: string;
|
||||
};
|
||||
}
|
||||
export interface AnonRequestPayload extends BasePayload {
|
||||
destinationHash: string;
|
||||
senderPublicKey: string;
|
||||
cipherMac: string;
|
||||
ciphertext: string;
|
||||
ciphertextLength: number;
|
||||
decrypted?: {
|
||||
timestamp: number;
|
||||
syncTimestamp?: number;
|
||||
password: string;
|
||||
};
|
||||
}
|
||||
export interface AckPayload extends BasePayload {
|
||||
checksum: string;
|
||||
}
|
||||
export interface PathPayload extends BasePayload {
|
||||
pathLength: number;
|
||||
pathHashSize?: number;
|
||||
pathHashes: string[];
|
||||
extraType: number;
|
||||
extraData: string;
|
||||
}
|
||||
export interface ResponsePayload extends BasePayload {
|
||||
destinationHash: string;
|
||||
sourceHash: string;
|
||||
cipherMac: string;
|
||||
ciphertext: string;
|
||||
ciphertextLength: number;
|
||||
decrypted?: {
|
||||
tag: number;
|
||||
content: string;
|
||||
};
|
||||
}
|
||||
export interface ControlPayloadBase extends BasePayload {
|
||||
subType: ControlSubType;
|
||||
rawFlags: number;
|
||||
}
|
||||
export interface ControlDiscoverReqPayload extends ControlPayloadBase {
|
||||
subType: ControlSubType.NodeDiscoverReq;
|
||||
prefixOnly: boolean;
|
||||
typeFilter: number;
|
||||
typeFilterNames: string[];
|
||||
tag: number;
|
||||
since: number;
|
||||
}
|
||||
export interface ControlDiscoverRespPayload extends ControlPayloadBase {
|
||||
subType: ControlSubType.NodeDiscoverResp;
|
||||
nodeType: DeviceRole;
|
||||
nodeTypeName: string;
|
||||
snr: number;
|
||||
tag: number;
|
||||
publicKey: string;
|
||||
publicKeyLength: number;
|
||||
}
|
||||
export type ControlPayload = ControlDiscoverReqPayload | ControlDiscoverRespPayload;
|
||||
export type PayloadData = AdvertPayload | TracePayload | GroupTextPayload | RequestPayload | TextMessagePayload | AnonRequestPayload | AckPayload | PathPayload | ResponsePayload | ControlPayload;
|
||||
//# sourceMappingURL=payloads.d.ts.map
|
||||
1
frontend/lib/meshcore-decoder/dist/types/payloads.d.ts.map
vendored
Normal file
1
frontend/lib/meshcore-decoder/dist/types/payloads.d.ts.map
vendored
Normal file
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"payloads.d.ts","sourceRoot":"","sources":["../../src/types/payloads.ts"],"names":[],"mappings":"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"}
|
||||
3
frontend/lib/meshcore-decoder/dist/types/payloads.js
vendored
Normal file
3
frontend/lib/meshcore-decoder/dist/types/payloads.js
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
//# sourceMappingURL=payloads.js.map
|
||||
1
frontend/lib/meshcore-decoder/dist/types/payloads.js.map
vendored
Normal file
1
frontend/lib/meshcore-decoder/dist/types/payloads.js.map
vendored
Normal file
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"payloads.js","sourceRoot":"","sources":["../../src/types/payloads.ts"],"names":[],"mappings":""}
|
||||
58
frontend/lib/meshcore-decoder/dist/utils/auth-token.d.ts
vendored
Normal file
58
frontend/lib/meshcore-decoder/dist/utils/auth-token.d.ts
vendored
Normal file
@@ -0,0 +1,58 @@
|
||||
/**
|
||||
* JWT-style token payload for MeshCore authentication
|
||||
*/
|
||||
export interface AuthTokenPayload {
|
||||
/** Public key of the signer (32 bytes hex) */
|
||||
publicKey: string;
|
||||
/** Unix timestamp when token was issued */
|
||||
iat: number;
|
||||
/** Unix timestamp when token expires (optional) */
|
||||
exp?: number;
|
||||
/** Audience claim (optional) */
|
||||
aud?: string;
|
||||
/** Custom claims */
|
||||
[key: string]: any;
|
||||
}
|
||||
/**
|
||||
* Encoded auth token structure
|
||||
*/
|
||||
export interface AuthToken {
|
||||
/** Base64url-encoded header */
|
||||
header: string;
|
||||
/** Base64url-encoded payload */
|
||||
payload: string;
|
||||
/** Hex-encoded Ed25519 signature */
|
||||
signature: string;
|
||||
}
|
||||
/**
|
||||
* Create a signed authentication token
|
||||
*
|
||||
* @param payload - Token payload containing claims
|
||||
* @param privateKeyHex - 64-byte private key in hex format (orlp/ed25519 format)
|
||||
* @param publicKeyHex - 32-byte public key in hex format
|
||||
* @returns JWT-style token string in format: header.payload.signature
|
||||
*/
|
||||
export declare function createAuthToken(payload: AuthTokenPayload, privateKeyHex: string, publicKeyHex: string): Promise<string>;
|
||||
/**
|
||||
* Verify and decode an authentication token
|
||||
*
|
||||
* @param token - JWT-style token string
|
||||
* @param expectedPublicKeyHex - Expected public key in hex format (optional, will check against payload if provided)
|
||||
* @returns Decoded payload if valid, null if invalid
|
||||
*/
|
||||
export declare function verifyAuthToken(token: string, expectedPublicKeyHex?: string): Promise<AuthTokenPayload | null>;
|
||||
/**
|
||||
* Parse a token without verifying (useful for debugging)
|
||||
*
|
||||
* @param token - JWT-style token string
|
||||
* @returns Parsed token structure or null if invalid format
|
||||
*/
|
||||
export declare function parseAuthToken(token: string): AuthToken | null;
|
||||
/**
|
||||
* Decode token payload without verification (useful for debugging)
|
||||
*
|
||||
* @param token - JWT-style token string
|
||||
* @returns Decoded payload or null if invalid format
|
||||
*/
|
||||
export declare function decodeAuthTokenPayload(token: string): AuthTokenPayload | null;
|
||||
//# sourceMappingURL=auth-token.d.ts.map
|
||||
1
frontend/lib/meshcore-decoder/dist/utils/auth-token.d.ts.map
vendored
Normal file
1
frontend/lib/meshcore-decoder/dist/utils/auth-token.d.ts.map
vendored
Normal file
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"auth-token.d.ts","sourceRoot":"","sources":["../../src/utils/auth-token.ts"],"names":[],"mappings":"AAMA;;GAEG;AACH,MAAM,WAAW,gBAAgB;IAC/B,8CAA8C;IAC9C,SAAS,EAAE,MAAM,CAAC;IAClB,2CAA2C;IAC3C,GAAG,EAAE,MAAM,CAAC;IACZ,mDAAmD;IACnD,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,gCAAgC;IAChC,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,oBAAoB;IACpB,CAAC,GAAG,EAAE,MAAM,GAAG,GAAG,CAAC;CACpB;AAED;;GAEG;AACH,MAAM,WAAW,SAAS;IACxB,+BAA+B;IAC/B,MAAM,EAAE,MAAM,CAAC;IACf,gCAAgC;IAChC,OAAO,EAAE,MAAM,CAAC;IAChB,oCAAoC;IACpC,SAAS,EAAE,MAAM,CAAC;CACnB;AAgDD;;;;;;;GAOG;AACH,wBAAsB,eAAe,CACnC,OAAO,EAAE,gBAAgB,EACzB,aAAa,EAAE,MAAM,EACrB,YAAY,EAAE,MAAM,GACnB,OAAO,CAAC,MAAM,CAAC,CAwCjB;AAED;;;;;;GAMG;AACH,wBAAsB,eAAe,CACnC,KAAK,EAAE,MAAM,EACb,oBAAoB,CAAC,EAAE,MAAM,GAC5B,OAAO,CAAC,gBAAgB,GAAG,IAAI,CAAC,CA0DlC;AAED;;;;;GAKG;AACH,wBAAgB,cAAc,CAAC,KAAK,EAAE,MAAM,GAAG,SAAS,GAAG,IAAI,CAe9D;AAED;;;;;GAKG;AACH,wBAAgB,sBAAsB,CAAC,KAAK,EAAE,MAAM,GAAG,gBAAgB,GAAG,IAAI,CAa7E"}
|
||||
194
frontend/lib/meshcore-decoder/dist/utils/auth-token.js
vendored
Normal file
194
frontend/lib/meshcore-decoder/dist/utils/auth-token.js
vendored
Normal file
@@ -0,0 +1,194 @@
|
||||
"use strict";
|
||||
// Copyright (c) 2025 Michael Hart: https://github.com/michaelhart/meshcore-decoder
|
||||
// MIT License
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.createAuthToken = createAuthToken;
|
||||
exports.verifyAuthToken = verifyAuthToken;
|
||||
exports.parseAuthToken = parseAuthToken;
|
||||
exports.decodeAuthTokenPayload = decodeAuthTokenPayload;
|
||||
const orlp_ed25519_wasm_1 = require("../crypto/orlp-ed25519-wasm");
|
||||
const hex_1 = require("./hex");
|
||||
/**
|
||||
* Base64url encode (URL-safe base64 without padding)
|
||||
*/
|
||||
function base64urlEncode(data) {
|
||||
// Convert to base64
|
||||
let base64 = '';
|
||||
if (typeof Buffer !== 'undefined') {
|
||||
// Node.js
|
||||
base64 = Buffer.from(data).toString('base64');
|
||||
}
|
||||
else {
|
||||
// Browser
|
||||
const binary = String.fromCharCode(...Array.from(data));
|
||||
base64 = btoa(binary);
|
||||
}
|
||||
// Make URL-safe and remove padding
|
||||
return base64
|
||||
.replace(/\+/g, '-')
|
||||
.replace(/\//g, '_')
|
||||
.replace(/=/g, '');
|
||||
}
|
||||
/**
|
||||
* Base64url decode
|
||||
*/
|
||||
function base64urlDecode(str) {
|
||||
// Add padding back
|
||||
let base64 = str.replace(/-/g, '+').replace(/_/g, '/');
|
||||
while (base64.length % 4) {
|
||||
base64 += '=';
|
||||
}
|
||||
if (typeof Buffer !== 'undefined') {
|
||||
// Node.js
|
||||
return new Uint8Array(Buffer.from(base64, 'base64'));
|
||||
}
|
||||
else {
|
||||
// Browser
|
||||
const binary = atob(base64);
|
||||
const bytes = new Uint8Array(binary.length);
|
||||
for (let i = 0; i < binary.length; i++) {
|
||||
bytes[i] = binary.charCodeAt(i);
|
||||
}
|
||||
return bytes;
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Create a signed authentication token
|
||||
*
|
||||
* @param payload - Token payload containing claims
|
||||
* @param privateKeyHex - 64-byte private key in hex format (orlp/ed25519 format)
|
||||
* @param publicKeyHex - 32-byte public key in hex format
|
||||
* @returns JWT-style token string in format: header.payload.signature
|
||||
*/
|
||||
async function createAuthToken(payload, privateKeyHex, publicKeyHex) {
|
||||
// Create header
|
||||
const header = {
|
||||
alg: 'Ed25519',
|
||||
typ: 'JWT'
|
||||
};
|
||||
// Ensure publicKey is in the payload (normalize to uppercase)
|
||||
if (!payload.publicKey) {
|
||||
payload.publicKey = publicKeyHex.toUpperCase();
|
||||
}
|
||||
else {
|
||||
payload.publicKey = payload.publicKey.toUpperCase();
|
||||
}
|
||||
// Ensure iat is set
|
||||
if (!payload.iat) {
|
||||
payload.iat = Math.floor(Date.now() / 1000);
|
||||
}
|
||||
// Encode header and payload
|
||||
const headerJson = JSON.stringify(header);
|
||||
const payloadJson = JSON.stringify(payload);
|
||||
const headerBytes = new TextEncoder().encode(headerJson);
|
||||
const payloadBytes = new TextEncoder().encode(payloadJson);
|
||||
const headerEncoded = base64urlEncode(headerBytes);
|
||||
const payloadEncoded = base64urlEncode(payloadBytes);
|
||||
// Create signing input: header.payload
|
||||
const signingInput = `${headerEncoded}.${payloadEncoded}`;
|
||||
const signingInputBytes = new TextEncoder().encode(signingInput);
|
||||
const signingInputHex = (0, hex_1.bytesToHex)(signingInputBytes);
|
||||
// Sign the input using the normalized public key from payload
|
||||
const signatureHex = await (0, orlp_ed25519_wasm_1.sign)(signingInputHex, privateKeyHex, payload.publicKey);
|
||||
// Return token in JWT format: header.payload.signature
|
||||
// Note: We use hex for signature instead of base64url for consistency with MeshCore
|
||||
return `${headerEncoded}.${payloadEncoded}.${signatureHex}`;
|
||||
}
|
||||
/**
|
||||
* Verify and decode an authentication token
|
||||
*
|
||||
* @param token - JWT-style token string
|
||||
* @param expectedPublicKeyHex - Expected public key in hex format (optional, will check against payload if provided)
|
||||
* @returns Decoded payload if valid, null if invalid
|
||||
*/
|
||||
async function verifyAuthToken(token, expectedPublicKeyHex) {
|
||||
try {
|
||||
// Parse token
|
||||
const parts = token.split('.');
|
||||
if (parts.length !== 3) {
|
||||
return null;
|
||||
}
|
||||
const [headerEncoded, payloadEncoded, signatureHex] = parts;
|
||||
// Decode header and payload
|
||||
const headerBytes = base64urlDecode(headerEncoded);
|
||||
const payloadBytes = base64urlDecode(payloadEncoded);
|
||||
const headerJson = new TextDecoder().decode(headerBytes);
|
||||
const payloadJson = new TextDecoder().decode(payloadBytes);
|
||||
const header = JSON.parse(headerJson);
|
||||
const payload = JSON.parse(payloadJson);
|
||||
// Validate header
|
||||
if (header.alg !== 'Ed25519' || header.typ !== 'JWT') {
|
||||
return null;
|
||||
}
|
||||
// Validate payload has required fields
|
||||
if (!payload.publicKey || !payload.iat) {
|
||||
return null;
|
||||
}
|
||||
// Check if expected public key matches
|
||||
if (expectedPublicKeyHex && payload.publicKey.toUpperCase() !== expectedPublicKeyHex.toUpperCase()) {
|
||||
return null;
|
||||
}
|
||||
// Check expiration if present
|
||||
if (payload.exp) {
|
||||
const now = Math.floor(Date.now() / 1000);
|
||||
if (now > payload.exp) {
|
||||
return null; // Token expired
|
||||
}
|
||||
}
|
||||
// Verify signature
|
||||
const signingInput = `${headerEncoded}.${payloadEncoded}`;
|
||||
const signingInputBytes = new TextEncoder().encode(signingInput);
|
||||
const signingInputHex = (0, hex_1.bytesToHex)(signingInputBytes);
|
||||
const isValid = await (0, orlp_ed25519_wasm_1.verify)(signatureHex, signingInputHex, payload.publicKey);
|
||||
if (!isValid) {
|
||||
return null;
|
||||
}
|
||||
return payload;
|
||||
}
|
||||
catch (error) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Parse a token without verifying (useful for debugging)
|
||||
*
|
||||
* @param token - JWT-style token string
|
||||
* @returns Parsed token structure or null if invalid format
|
||||
*/
|
||||
function parseAuthToken(token) {
|
||||
try {
|
||||
const parts = token.split('.');
|
||||
if (parts.length !== 3) {
|
||||
return null;
|
||||
}
|
||||
return {
|
||||
header: parts[0],
|
||||
payload: parts[1],
|
||||
signature: parts[2]
|
||||
};
|
||||
}
|
||||
catch (error) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Decode token payload without verification (useful for debugging)
|
||||
*
|
||||
* @param token - JWT-style token string
|
||||
* @returns Decoded payload or null if invalid format
|
||||
*/
|
||||
function decodeAuthTokenPayload(token) {
|
||||
try {
|
||||
const parts = token.split('.');
|
||||
if (parts.length !== 3) {
|
||||
return null;
|
||||
}
|
||||
const payloadBytes = base64urlDecode(parts[1]);
|
||||
const payloadJson = new TextDecoder().decode(payloadBytes);
|
||||
return JSON.parse(payloadJson);
|
||||
}
|
||||
catch (error) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
//# sourceMappingURL=auth-token.js.map
|
||||
1
frontend/lib/meshcore-decoder/dist/utils/auth-token.js.map
vendored
Normal file
1
frontend/lib/meshcore-decoder/dist/utils/auth-token.js.map
vendored
Normal file
File diff suppressed because one or more lines are too long
26
frontend/lib/meshcore-decoder/dist/utils/enum-names.d.ts
vendored
Normal file
26
frontend/lib/meshcore-decoder/dist/utils/enum-names.d.ts
vendored
Normal file
@@ -0,0 +1,26 @@
|
||||
import { RouteType, PayloadType, PayloadVersion, DeviceRole, RequestType, ControlSubType } from '../types/enums';
|
||||
/**
|
||||
* Get human-readable name for RouteType enum value
|
||||
*/
|
||||
export declare function getRouteTypeName(routeType: RouteType): string;
|
||||
/**
|
||||
* Get human-readable name for PayloadType enum value
|
||||
*/
|
||||
export declare function getPayloadTypeName(payloadType: PayloadType): string;
|
||||
/**
|
||||
* Get human-readable name for PayloadVersion enum value
|
||||
*/
|
||||
export declare function getPayloadVersionName(version: PayloadVersion): string;
|
||||
/**
|
||||
* Get human-readable name for DeviceRole enum value
|
||||
*/
|
||||
export declare function getDeviceRoleName(role: DeviceRole): string;
|
||||
/**
|
||||
* Get human-readable name for RequestType enum value
|
||||
*/
|
||||
export declare function getRequestTypeName(requestType: RequestType): string;
|
||||
/**
|
||||
* Get human-readable name for ControlSubType enum value
|
||||
*/
|
||||
export declare function getControlSubTypeName(subType: ControlSubType): string;
|
||||
//# sourceMappingURL=enum-names.d.ts.map
|
||||
1
frontend/lib/meshcore-decoder/dist/utils/enum-names.d.ts.map
vendored
Normal file
1
frontend/lib/meshcore-decoder/dist/utils/enum-names.d.ts.map
vendored
Normal file
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"enum-names.d.ts","sourceRoot":"","sources":["../../src/utils/enum-names.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,SAAS,EAAE,WAAW,EAAE,cAAc,EAAE,UAAU,EAAE,WAAW,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC;AAEjH;;GAEG;AACH,wBAAgB,gBAAgB,CAAC,SAAS,EAAE,SAAS,GAAG,MAAM,CAQ7D;AAED;;GAEG;AACH,wBAAgB,kBAAkB,CAAC,WAAW,EAAE,WAAW,GAAG,MAAM,CAiBnE;AAED;;GAEG;AACH,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,cAAc,GAAG,MAAM,CAQrE;AAED;;GAEG;AACH,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,UAAU,GAAG,MAAM,CAS1D;AAED;;GAEG;AACH,wBAAgB,kBAAkB,CAAC,WAAW,EAAE,WAAW,GAAG,MAAM,CASnE;AAED;;GAEG;AACH,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,cAAc,GAAG,MAAM,CAMrE"}
|
||||
93
frontend/lib/meshcore-decoder/dist/utils/enum-names.js
vendored
Normal file
93
frontend/lib/meshcore-decoder/dist/utils/enum-names.js
vendored
Normal file
@@ -0,0 +1,93 @@
|
||||
"use strict";
|
||||
// Copyright (c) 2025 Michael Hart: https://github.com/michaelhart/meshcore-decoder
|
||||
// MIT License
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.getRouteTypeName = getRouteTypeName;
|
||||
exports.getPayloadTypeName = getPayloadTypeName;
|
||||
exports.getPayloadVersionName = getPayloadVersionName;
|
||||
exports.getDeviceRoleName = getDeviceRoleName;
|
||||
exports.getRequestTypeName = getRequestTypeName;
|
||||
exports.getControlSubTypeName = getControlSubTypeName;
|
||||
const enums_1 = require("../types/enums");
|
||||
/**
|
||||
* Get human-readable name for RouteType enum value
|
||||
*/
|
||||
function getRouteTypeName(routeType) {
|
||||
switch (routeType) {
|
||||
case enums_1.RouteType.Flood: return 'Flood';
|
||||
case enums_1.RouteType.Direct: return 'Direct';
|
||||
case enums_1.RouteType.TransportFlood: return 'TransportFlood';
|
||||
case enums_1.RouteType.TransportDirect: return 'TransportDirect';
|
||||
default: return `Unknown (${routeType})`;
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Get human-readable name for PayloadType enum value
|
||||
*/
|
||||
function getPayloadTypeName(payloadType) {
|
||||
switch (payloadType) {
|
||||
case enums_1.PayloadType.RawCustom: return 'RawCustom';
|
||||
case enums_1.PayloadType.Trace: return 'Trace';
|
||||
case enums_1.PayloadType.Advert: return 'Advert';
|
||||
case enums_1.PayloadType.GroupText: return 'GroupText';
|
||||
case enums_1.PayloadType.GroupData: return 'GroupData';
|
||||
case enums_1.PayloadType.Request: return 'Request';
|
||||
case enums_1.PayloadType.Response: return 'Response';
|
||||
case enums_1.PayloadType.TextMessage: return 'TextMessage';
|
||||
case enums_1.PayloadType.AnonRequest: return 'AnonRequest';
|
||||
case enums_1.PayloadType.Ack: return 'Ack';
|
||||
case enums_1.PayloadType.Path: return 'Path';
|
||||
case enums_1.PayloadType.Multipart: return 'Multipart';
|
||||
case enums_1.PayloadType.Control: return 'Control';
|
||||
default: return `Unknown (0x${payloadType.toString(16)})`;
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Get human-readable name for PayloadVersion enum value
|
||||
*/
|
||||
function getPayloadVersionName(version) {
|
||||
switch (version) {
|
||||
case enums_1.PayloadVersion.Version1: return 'Version 1';
|
||||
case enums_1.PayloadVersion.Version2: return 'Version 2';
|
||||
case enums_1.PayloadVersion.Version3: return 'Version 3';
|
||||
case enums_1.PayloadVersion.Version4: return 'Version 4';
|
||||
default: return `Unknown (${version})`;
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Get human-readable name for DeviceRole enum value
|
||||
*/
|
||||
function getDeviceRoleName(role) {
|
||||
switch (role) {
|
||||
case enums_1.DeviceRole.Unknown: return 'Unknown';
|
||||
case enums_1.DeviceRole.ChatNode: return 'Chat Node';
|
||||
case enums_1.DeviceRole.Repeater: return 'Repeater';
|
||||
case enums_1.DeviceRole.RoomServer: return 'Room Server';
|
||||
case enums_1.DeviceRole.Sensor: return 'Sensor';
|
||||
default: return `Unknown (${role})`;
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Get human-readable name for RequestType enum value
|
||||
*/
|
||||
function getRequestTypeName(requestType) {
|
||||
switch (requestType) {
|
||||
case enums_1.RequestType.GetStats: return 'Get Stats';
|
||||
case enums_1.RequestType.Keepalive: return 'Keepalive (deprecated)';
|
||||
case enums_1.RequestType.GetTelemetryData: return 'Get Telemetry Data';
|
||||
case enums_1.RequestType.GetMinMaxAvgData: return 'Get Min/Max/Avg Data';
|
||||
case enums_1.RequestType.GetAccessList: return 'Get Access List';
|
||||
default: return `Unknown (${requestType})`;
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Get human-readable name for ControlSubType enum value
|
||||
*/
|
||||
function getControlSubTypeName(subType) {
|
||||
switch (subType) {
|
||||
case enums_1.ControlSubType.NodeDiscoverReq: return 'Node Discover Request';
|
||||
case enums_1.ControlSubType.NodeDiscoverResp: return 'Node Discover Response';
|
||||
default: return `Unknown (0x${subType.toString(16)})`;
|
||||
}
|
||||
}
|
||||
//# sourceMappingURL=enum-names.js.map
|
||||
1
frontend/lib/meshcore-decoder/dist/utils/enum-names.js.map
vendored
Normal file
1
frontend/lib/meshcore-decoder/dist/utils/enum-names.js.map
vendored
Normal file
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"enum-names.js","sourceRoot":"","sources":["../../src/utils/enum-names.ts"],"names":[],"mappings":"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"}
|
||||
17
frontend/lib/meshcore-decoder/dist/utils/hex.d.ts
vendored
Normal file
17
frontend/lib/meshcore-decoder/dist/utils/hex.d.ts
vendored
Normal file
@@ -0,0 +1,17 @@
|
||||
/**
|
||||
* Convert a single byte to uppercase hex string
|
||||
*/
|
||||
export declare function byteToHex(byte: number): string;
|
||||
/**
|
||||
* Convert a Uint8Array to uppercase hex string
|
||||
*/
|
||||
export declare function bytesToHex(bytes: Uint8Array): string;
|
||||
/**
|
||||
* Convert a number to uppercase hex string with specified padding
|
||||
*/
|
||||
export declare function numberToHex(num: number, padLength?: number): string;
|
||||
/**
|
||||
* Convert hex string to Uint8Array
|
||||
*/
|
||||
export declare function hexToBytes(hex: string): Uint8Array;
|
||||
//# sourceMappingURL=hex.d.ts.map
|
||||
1
frontend/lib/meshcore-decoder/dist/utils/hex.d.ts.map
vendored
Normal file
1
frontend/lib/meshcore-decoder/dist/utils/hex.d.ts.map
vendored
Normal file
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"hex.d.ts","sourceRoot":"","sources":["../../src/utils/hex.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,wBAAgB,SAAS,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAE9C;AAED;;GAEG;AACH,wBAAgB,UAAU,CAAC,KAAK,EAAE,UAAU,GAAG,MAAM,CAEpD;AAED;;GAEG;AACH,wBAAgB,WAAW,CAAC,GAAG,EAAE,MAAM,EAAE,SAAS,GAAE,MAAU,GAAG,MAAM,CAEtE;AAED;;GAEG;AACH,wBAAgB,UAAU,CAAC,GAAG,EAAE,MAAM,GAAG,UAAU,CAmBlD"}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user