58 lines
3.4 KiB
Markdown
58 lines
3.4 KiB
Markdown
# Initial managed API contract
|
|
|
|
Status: initial port slice, API revision 1. No change to protobuf or media wire formats.
|
|
These are shared infrastructure APIs; the client-facing API follows with the client core.
|
|
|
|
## Protocol
|
|
|
|
`VoiceCat.Protocol` generates `Voicecat.V1` protobuf messages from the existing schema.
|
|
|
|
`ControlFraming.TryReadFrame(ref ReadOnlySequence<byte>, out ReadOnlySequence<byte>)`
|
|
extracts a payload and advances input only when a full frame exists. Returned memory
|
|
borrows the input's lifetime. Lengths above 16 MiB throw `InvalidDataException`.
|
|
Empty payloads are valid. `WriteFrame` and `WriteEnvelope` target `IBufferWriter<byte>`;
|
|
oversized outgoing payloads throw before output is written.
|
|
|
|
`ReadEnvelopesAsync(PipeReader, CancellationToken)` produces parsed envelopes and
|
|
advances consumed pipe data. It does not complete or dispose the caller's reader.
|
|
Clean EOF ends enumeration; partial EOF and oversized frames throw
|
|
`InvalidDataException`; malformed protobuf throws `InvalidProtocolBufferException`.
|
|
Cancellation propagates. A connection owner must close on protocol errors or
|
|
cancellation partway through a frame; partial frame bytes may already be consumed.
|
|
Fragments are consumed as they arrive so frames larger than pipe backpressure
|
|
thresholds make progress. Stopping enumeration between envelopes preserves the next frame.
|
|
|
|
`VoiceFrameHeader` is an immutable value with type, flags, codec, SSRC, sequence,
|
|
and timestamp. `Write(Span<byte>)` writes its 20-byte big-endian representation;
|
|
`TryRead` accepts at least 20 bytes and preserves unknown type/flag/codec values.
|
|
Higher layers decide which values they support.
|
|
|
|
## Media encryption
|
|
|
|
`MediaEncryptor` and `MediaDecryptor` each own one directional 32-byte session key
|
|
and mutable packet state. Use one owner at a time; they provide no synchronization.
|
|
Production constructs them from TLS exporter keys when TLS is implemented. Raw-key
|
|
constructors support conformance tests and the future TLS integration.
|
|
|
|
`MediaEncryptor.Encrypt(VoiceFrameHeader, ReadOnlySpan<byte>, Span<byte>)` writes
|
|
the full header plus ciphertext and 16-byte tag and returns packet length. It replaces
|
|
the supplied sequence with its own counter, starting at zero. Capacity and overlap
|
|
errors throw before reserving a counter. Reserved counters are never reused after
|
|
encryption failure. At `ulong.MaxValue`, encryption throws and requires a new session.
|
|
|
|
`MediaDecryptor.TryDecrypt(ReadOnlySpan<byte>, Span<byte>, out VoiceFrameHeader,
|
|
out int)` authenticates and decrypts a complete packet. Short packets, failed tags,
|
|
replays, and packets outside the 64-packet window return false with default header
|
|
and zero bytes written. Authentication failure clears the attempted plaintext region;
|
|
structural/replay rejection leaves storage untouched. Callers must only consume
|
|
output after success. Invalid storage capacity and overlapping buffers throw.
|
|
|
|
The nonce is four zero bytes plus the big-endian header counter. All 20 header bytes
|
|
are authenticated associated data. The replay window advances after authentication.
|
|
The platform ChaCha20-Poly1305 implementation is preferred; BouncyCastle is used when
|
|
platform support is absent. Both produce the same wire bytes. The fallback currently
|
|
allocates per packet; audio and relay allocation guarantees are later checkpoints.
|
|
|
|
Dispose both objects to clear their owned key arrays and release platform crypto
|
|
resources. Use after disposal throws `ObjectDisposedException`.
|