[Fix] Harden MTP codec, transport, and SDK security

This commit is contained in:
Alex Emmet 2026-08-18 20:57:45 +02:00
commit a7e804c603
No known key found for this signature in database
73 changed files with 11892 additions and 5756 deletions

View file

@ -1,6 +1,8 @@
# Type Map
This file documents the Type Map & Registry configuration used by the MTP protocol. It will assume you are working with the [example-type-maps.yaml](./../example-type-maps.yaml).
A type musn't be the version of MTP, it stays independant.
MTP version defines the codec. The Type-Map version defines the available Types.
## Binary Frame Format
@ -85,6 +87,15 @@ The envelope length counts the bytes after the length field. A recipient entry i
Protection nesting directly represents both signer-visibility choices: `Encrypted(Signed(Container))` keeps signer metadata private, while `Signed(Encrypted(Container))` exposes it. A frame with no outer sender and an `Encrypted(Signed(Container))` payload uses sealed sender. Sealed sender adds no flag or distinct wire type.
### Container ordering and signatures
Container entries are ordered sequences in the current format. Insertion order
is therefore semantic: two containers with the same field/value pairs in a
different order have different serialized bytes and different signatures. The
decoder rejects duplicate field IDs. Applications that need map semantics must
canonicalize their own input before signing; a future canonical map encoding
requires a protocol-format version and cannot be inferred by a receiver.
## TypeMap & Compile-Time Type Safety
A `TypeMap` maps Communication-Types and Data-Types to their wire IDs. Each protocol version has its own `TypeMap` because the same type name may use different wire IDs in different versions.
@ -175,17 +186,33 @@ mtp = { path = "..", features = ["host"] }
```rust
use mtp::codec::registry::{Registry, VersionedCodec};
use mtp::codec::{CommunicationType, CommunicationValue, DataValue};
use mtp_type_map::Version;
let registry = Registry::builtin();
let codec = VersionedCodec::new(registry);
let codec = VersionedCodec::for_version(registry, Version(3, 0)).unwrap();
let value = CommunicationValue::new_with_type_map(
CommunicationType::Ping,
codec.type_map(),
).with_payload(DataValue::Null);
// Encode with a specific version
let bytes = codec.encode(&value, Version(3, 0)).unwrap();
// The value must retain the negotiated map used to construct it.
let bytes = codec.encode(&value).unwrap();
// Decode with a specific version
let decoded = codec.decode(&bytes, Version(3, 0)).unwrap();
let decoded = codec.decode(&bytes).unwrap();
// A clear value can be migrated explicitly when the application has chosen
// that behavior. Protected values are not silently remapped.
let migrated = codec.encode_migrating(&value).unwrap();
```
`VersionedCodec::encode` compares the retained map identity (its protocol
version) and returns `CodecError::MissingTypeMap` or
`CodecError::TypeMapMismatch` on failure. `reply_to` retains the request's
map, while `try_merge` rejects frames from different maps before copying any
fields. The deprecated `merge` method records the error for compatibility; new
code should migrate to `try_merge` and handle the result.
## Customizing Type Maps in Downstream Projects
External projects must provide their own type map configuration. Browser projects use the Vite plugin from [Defining Type Maps](#defining-type-maps) and do not need to publish, fork, or copy a generated WASM package.