Files
re-teamspeak/docs/SDD.md
T
ReTeamSpeak ea08823c97
CI/CD / Build Frontend (push) Failing after 11s
CI/CD / Test (macos-latest) (push) Has been cancelled
CI/CD / Test (windows-latest) (push) Has been cancelled
CI/CD / Build Desktop (linux) (push) Has been cancelled
CI/CD / Build Desktop (macos) (push) Has been cancelled
CI/CD / Build Desktop (windows) (push) Has been cancelled
CI/CD / Release (push) Has been cancelled
CI/CD / Test (ubuntu-latest) (push) Failing after 2s
Initial commit: ReTeamSpeak cross-platform TeamSpeak client
- tscore: Protocol implementation (packets, crypto, connection handshake)
- tsaudio: Audio engine (capture, playback, codec, VAD, jitter buffer)
- tsdb: SQLite database (identities, bookmarks, messages, settings)
- shared: Core types and events
- tauri-app: Tauri v2 desktop application with React frontend
- docs: SRS, SAD, SDD documentation
- CI/CD: GitHub Actions workflow
- 32 unit tests passing
2026-05-12 14:41:54 +09:00

31 KiB

Software Design Document (SDD)

ReTeamSpeak - Cross-Platform TeamSpeak Client

Version: 1.0.0
Date: 2026-05-12
Status: Based on actual implementation


1. Detailed Design

1.1 shared Module Design

1.1.1 Core Type System

┌─────────────────────────────────────────────────────────────┐
│                    Type Hierarchy                             │
├─────────────────────────────────────────────────────────────┤
│  Identifier Types (Newtype Pattern)                          │
│  ├── ClientId(u16)        // In-session client ID           │
│  ├── ChannelId(u64)       // Channel identifier             │
│  ├── ServerGroupId(u64)   // Server group                   │
│  ├── ChannelGroupId(u64)  // Channel group                  │
│  ├── ClientDbId(u64)      // Database client ID             │
│  ├── Uid(String)          // Unique identity (base64)       │
│  ├── PermissionId(u32)    // Permission ID                  │
│  └── IconId(i32)          // Icon identifier                │
│                                                              │
│  Enumerations                                                │
│  ├── Codec { SpeexNB, SpeexWB, SpeexUWB, Celt, OpusVoice,  │
│  │          OpusMusic }                                      │
│  ├── ChannelType { Permanent, SemiPermanent, Temporary }    │
│  ├── ClientType { Normal, Query { admin } }                 │
│  ├── ConnectionState { Disconnected, Connecting, ... }      │
│  ├── Reason { None, Moved, LostConnection, KickChannel, ... }│
│  ├── CodecEncryptionMode { PerChannel, ForcedOff, ForcedOn }│
│  ├── HostMessageMode { None, Log, Modal, Modalquit }        │
│  ├── GroupType { Template, Regular, Query }                 │
│  └── GroupNamingMode { None, Before, After }                │
│                                                              │
│  Data Structures                                             │
│  ├── ServerInfo { id, name, platform, version, max_clients, │
│  │               clients_online, ... }                       │
│  ├── ChannelInfo { id, parent_id, name, codec, max_clients, │
│  │                channel_type, ... }                        │
│  ├── ClientInfo { id, channel_id, uid, name, muted, ... }   │
│  ├── ChatMessage { id, timestamp, invoker, target, message }│
│  └── AppConfig { nickname, audio, hotkeys, theme, ... }     │
└─────────────────────────────────────────────────────────────┘

1.1.2 Event System Design

AppEvent
├── Connection(ConnectionEvent)
│   ├── Connecting { address }
│   ├── Connected { server, own_client }
│   ├── StateChanged { state }
│   ├── DisconnectedTemporarily { reason }
│   ├── Disconnected { reason }
│   └── ConnectionFailed { error }
│
├── Client(ClientEvent)
│   ├── EnteredView { client, reason }
│   ├── LeftView { client_id, reason, reason_message }
│   ├── Updated { client_id, changes: ClientChanges }
│   ├── Moved { client_id, from_channel, to_channel, reason }
│   ├── StartedTalking { client_id }
│   ├── StoppedTalking { client_id }
│   ├── ServerGroupChanged { client_id, group_id, added }
│   └── ChannelGroupChanged { client_id, group_id }
│
├── Channel(ChannelEvent)
│   ├── Created { channel }
│   ├── Deleted { channel_id }
│   ├── Updated { channel_id, changes: ChannelChanges }
│   ├── Moved { channel_id, new_parent, new_order }
│   ├── PasswordChanged { channel_id }
│   ├── DescriptionChanged { channel_id }
│   └── Subscribed { channel_id, subscribed }
│
├── Server(ServerEvent)
│   ├── Updated { changes: ServerChanges }
│   ├── ServerGroupList { groups }
│   └── ChannelGroupList { groups }
│
├── Message(MessageEvent)
│   ├── Received { message }
│   ├── Sent { message }
│   ├── Read { message_id }
│   └── UnreadCountChanged { count }
│
├── Audio(AudioEvent)
│   ├── InputDeviceChanged { device }
│   ├── OutputDeviceChanged { device }
│   ├── InputVolumeChanged { volume }
│   ├── OutputVolumeChanged { volume }
│   ├── InputMutedChanged { muted }
│   ├── OutputMutedChanged { muted }
│   ├── DeviceList { input_devices, output_devices }
│   ├── InputLevel { level }
│   └── OutputLevel { level }
│
├── FileTransfer(FileTransferEvent)
│   ├── Started { transfer_id, file_name, file_size, is_upload }
│   ├── Progress { transfer_id, progress }
│   ├── Completed { transfer_id }
│   ├── Failed { transfer_id, error }
│   └── Cancelled { transfer_id }
│
└── Error(ErrorEvent)
    ├── Protocol { code, message }
    ├── Network { message }
    ├── Audio { message }
    ├── Database { message }
    └── Other { message }

1.2 tscore Module Design

1.2.1 Packet Processing Pipeline

                    SEND                              RECEIVE
┌──────────────────────────────┐    ┌──────────────────────────────┐
│  Command String              │    │  UDP Packet                  │
│       │                      │    │       │                      │
│       ▼                      │    │       ▼                      │
│  Command::serialize()        │    │  InPacket::parse()           │
│       │                      │    │       │                      │
│       ▼                      │    │       ▼                      │
│  QuickLZ compress            │    │  AES-EAX decrypt             │
│  (if Command/CommandLow)     │    │  (or fake decrypt)           │
│       │                      │    │       │                      │
│       ▼                      │    │       ▼                      │
│  Fragment (if > 500 bytes)   │    │  QuickLZ decompress          │
│       │                      │    │  (if COMPRESSED flag)        │
│       ▼                      │    │       │                      │
│  AES-EAX encrypt             │    │       ▼                      │
│  (or fake encrypt)           │    │  Defragment                  │
│       │                      │    │  (if FRAGMENTED flag)        │
│       ▼                      │    │       │                      │
│  Assign Packet ID            │    │       ▼                      │
│       │                      │    │  Command::parse()            │
│       ▼                      │    │       │                      │
│  UDP Send                    │    │       ▼                      │
└──────────────────────────────┘    │  Application Layer           │
                                    └──────────────────────────────┘

1.2.2 Encryption Key Derivation

Input:  packet_type, direction, generation_id, shared_iv[64]
        │
        ▼
┌───────────────────────────────────────────────────────┐
│  temp[0] = direction_byte  (0x30=S2C, 0x31=C2S)     │
│  temp[1] = packet_type.u8()                          │
│  temp[2..6] = generation_id.to_be_bytes()            │
│  temp[6..70] = shared_iv[0..64]                      │
│                                                       │
│  key_nonce = SHA-256(temp)                            │
│  key = key_nonce[0..16]                               │
│  nonce = key_nonce[16..32]                            │
│                                                       │
│  key[0] ^= (packet_id >> 8) as u8                    │
│  key[1] ^= (packet_id & 0xFF) as u8                  │
└───────────────────────────────────────────────────────┘
        │
        ▼
Output: key[16], nonce[16]  →  AES-128-EAX

1.2.3 Connection State Machine

                    ┌──────────────┐
                    │ Disconnected │
                    └──────┬───────┘
                           │ start_handshake()
                           ▼
                    ┌──────────────┐
            ┌──────│  Connecting  │◄─────────────────┐
            │      └──────┬───────┘                  │
            │             │ Init1 received           │
            │             ▼                          │
            │      ┌──────────────────────┐          │
            │      │ IdentityLevelIncreasing│         │
            │      └──────┬───────────────┘          │
            │             │ Init3 received           │
            │             ▼                          │
            │      ┌──────────────┐                  │
            │      │  Connected   │──────────────────┤
            │      └──────┬───────┘                  │
            │             │ channellistfinished      │
            │             ▼                          │
            │      ┌─────────────────────┐           │
            │      │ ChannelListFinished │           │
            │      └──────┬──────────────┘           │
            │             │                          │
            │             ▼                          │
            │      ┌──────────────────────────┐      │
            └─────>│ DisconnectedTemporarily  │──────┘
                   └──────┬───────────────────┘
                          │ timeout / manual
                          ▼
                   ┌──────────────┐
                   │    Error     │
                   └──────┬───────┘
                          │
                          ▼
                   ┌──────────────┐
                   │ Disconnected │
                   └──────────────┘

1.2.4 RSA Puzzle Solver

/// Solves y = x^(2^level) mod n
/// 
/// Algorithm:
///   y = x
///   for i in 0..level:
///       y = (y * y) mod n
///
/// Time complexity: O(level * M(n)) where M(n) is multiplication cost
/// Space complexity: O(n) for big integer storage
fn solve_rsa_puzzle(x: &[u8; 64], n: &[u8; 64], level: u32) -> [u8; 64] {
    let x_big = BigUint::from_bytes_be(x);
    let n_big = BigUint::from_bytes_be(n);
    let mut y = x_big;
    for _ in 0..level {
        y = (y.clone() * y) % &n_big;
    }
    // Convert back to 64-byte array (big-endian, zero-padded)
}

1.2.5 Retransmission System

┌─────────────────────────────────────────────────────────┐
│                   ResendManager                          │
├─────────────────────────────────────────────────────────┤
│  pending: BTreeMap<PacketId, SentPacket>                │
│  max_retries: u32 (default: 10)                         │
│  connection_timeout: Duration (default: 30s)            │
├─────────────────────────────────────────────────────────┤
│  add_sent(id, data)     → Add to pending queue          │
│  ack(id) → bool         → Remove from pending           │
│  get_retransmissions()  → Vec<(id, data)> to resend     │
│  is_connection_timeout()→ Check for dead connection     │
├─────────────────────────────────────────────────────────┤
│                                                          │
│  SentPacket:                                             │
│    data: Vec<u8>                                         │
│    sent_at: Instant                                      │
│    retry_count: u32                                      │
│    timeout: Duration  (starts at 500ms, doubles)         │
│                                                          │
│  RttEstimator:                                           │
│    srtt: Duration    (smoothed RTT)                      │
│    rtt_var: Duration (RTT variance)                      │
│    rto: Duration     (retransmission timeout)            │
│    update(measured_rtt) → recalculate SRTT, RTO          │
└─────────────────────────────────────────────────────────┘

1.3 tsaudio Module Design

1.3.1 Audio Pipeline

CAPTURE PIPELINE:
┌──────────┐    ┌──────────┐    ┌──────────┐    ┌──────────┐
│   cpal   │───>│   VAD    │───>│  Opus    │───>│  Packet  │
│  capture │    │  detect  │    │  encode  │    │  output  │
└──────────┘    └──────────┘    └──────────┘    └──────────┘

PLAYBACK PIPELINE:
┌──────────┐    ┌──────────┐    ┌──────────┐    ┌──────────┐
│  Packet  │───>│  Jitter  │───>│  Opus    │───>│   cpal   │
│  input   │    │  buffer  │    │  decode  │    │ playback │
└──────────┘    └──────────┘    └──────────┘    └──────────┘

1.3.2 Voice Activity Detection

pub struct VadDetector {
    threshold: f32,
    state: VadState,
}

impl VadDetector {
    pub fn detect(&mut self, samples: &[f32]) -> VadState {
        let energy = samples.iter().map(|s| s * s).sum::<f32>() 
                   / samples.len() as f32;
        if energy > self.threshold {
            self.state = VadState::Speaking;
        } else {
            self.state = VadState::Silent;
        }
        self.state
    }
}

1.3.3 Jitter Buffer

┌─────────────────────────────────────────────────────────┐
│                    JitterBuffer                          │
├─────────────────────────────────────────────────────────┤
│  buffer: Vec<Option<AudioFrame>>  (ring buffer)         │
│  head: usize                                            │
│  tail: usize                                            │
│  size: usize                                            │
│  capacity: usize                                        │
├─────────────────────────────────────────────────────────┤
│  push(frame) → Result<()>    // Add frame               │
│  pop() → Option<AudioFrame>  // Get next frame          │
│  len() → usize               // Current buffer size     │
│  is_empty() → bool                                        │
│  is_full() → bool                                         │
│  clear()                     // Reset buffer             │
└─────────────────────────────────────────────────────────┘

1.4 tsdb Module Design

1.4.1 Database Schema

-- Identity storage
CREATE TABLE identities (
    id TEXT PRIMARY KEY,           -- UUID
    name TEXT NOT NULL,            -- Display name
    private_key TEXT NOT NULL,     -- Base64 encoded ECC private key
    counter INTEGER DEFAULT 0,     -- Hash Cash counter
    max_counter INTEGER DEFAULT 0, -- Maximum counter tried
    created_at TEXT NOT NULL,      -- ISO 8601 timestamp
    updated_at TEXT NOT NULL       -- ISO 8601 timestamp
);

-- Server bookmarks
CREATE TABLE bookmarks (
    id TEXT PRIMARY KEY,           -- UUID
    name TEXT NOT NULL,            -- Display name
    address TEXT NOT NULL,         -- Server address
    port INTEGER DEFAULT 9987,     -- Server port
    nickname TEXT,                 -- Preferred nickname
    server_password TEXT,          -- Encrypted server password
    channel TEXT,                  -- Default channel
    channel_password TEXT,         -- Encrypted channel password
    default_token TEXT,            -- Permission token
    auto_connect INTEGER DEFAULT 0,-- Auto-connect on startup
    last_connected TEXT,           -- Last connection timestamp
    created_at TEXT NOT NULL,
    updated_at TEXT NOT NULL
);

-- Message history
CREATE TABLE messages (
    id INTEGER PRIMARY KEY AUTOINCREMENT,
    server_address TEXT NOT NULL,  -- Server address
    invoker_id INTEGER NOT NULL,   -- Client ID
    invoker_name TEXT NOT NULL,    -- Display name
    invoker_uid TEXT NOT NULL,     -- Unique ID
    target_type TEXT NOT NULL,     -- "server", "channel", "client"
    target_id INTEGER,             -- Target ID
    message TEXT NOT NULL,         -- Message content
    is_read INTEGER DEFAULT 0,     -- Read status
    timestamp TEXT NOT NULL        -- ISO 8601 timestamp
);

-- Key-value settings
CREATE TABLE settings (
    key TEXT PRIMARY KEY,
    value TEXT NOT NULL,
    updated_at TEXT NOT NULL
);

1.4.2 CRUD Operations

impl DatabaseManager {
    // Identity operations
    fn create_identity(&self, name, private_key) -> DatabaseResult<Identity>;
    fn get_identity(&self, id) -> DatabaseResult<Identity>;
    fn get_all_identities(&self) -> DatabaseResult<Vec<Identity>>;
    fn update_identity(&self, id, name?, counter?) -> DatabaseResult<()>;
    fn delete_identity(&self, id) -> DatabaseResult<()>;

    // Bookmark operations
    fn create_bookmark(&self, name, address, port, nickname?) -> DatabaseResult<Bookmark>;
    fn get_bookmark(&self, id) -> DatabaseResult<Bookmark>;
    fn get_all_bookmarks(&self) -> DatabaseResult<Vec<Bookmark>>;
    fn update_bookmark(&self, id, name?, address?, port?, nickname?) -> DatabaseResult<()>;
    fn delete_bookmark(&self, id) -> DatabaseResult<()>;

    // Message operations
    fn create_message(&self, server_address, invoker_id, invoker_name, 
                      invoker_uid, target_type, target_id?, message) -> DatabaseResult<Message>;
    fn get_server_messages(&self, server_address, limit, offset) -> DatabaseResult<Vec<Message>>;
    fn mark_message_read(&self, id) -> DatabaseResult<()>;

    // Settings operations
    fn get_setting(&self, key) -> DatabaseResult<Option<String>>;
    fn set_setting(&self, key, value) -> DatabaseResult<()>;
}

1.5 tauri-app Design

1.5.1 Tauri Command Architecture

┌─────────────────────────────────────────────────────────────┐
│                    Frontend (React)                          │
│  ┌───────────────────────────────────────────────────────┐  │
│  │  invoke("get_bookmarks")  →  Promise<BookmarkInfo[]> │  │
│  │  invoke("connect", {addr, port, nick, pass})         │  │
│  │  invoke("send_message", {target, message})           │  │
│  └───────────────────────────────────────────────────────┘  │
└───────────────────────────┬─────────────────────────────────┘
                            │ Tauri IPC
                            ▼
┌─────────────────────────────────────────────────────────────┐
│                    Tauri Shell (Rust)                        │
│  ┌───────────────────────────────────────────────────────┐  │
│  │  #[tauri::command]                                    │  │
│  │  async fn get_bookmarks(state: State<AppState>)       │  │
│  │      -> Result<Vec<BookmarkInfo>, String>              │  │
│  │  {                                                    │  │
│  │      state.db.get_all_bookmarks()                     │  │
│  │          .map(|b| b.into_iter().map(Into::into))      │  │
│  │          .map_err(|e| e.to_string())                  │  │
│  │  }                                                    │  │
│  └───────────────────────────────────────────────────────┘  │
│  ┌───────────────────────────────────────────────────────┐  │
│  │  AppState {                                           │  │
│  │      db: DatabaseManager,                             │  │
│  │      connection_state: Mutex<ConnectionState>,        │  │
│  │  }                                                    │  │
│  └───────────────────────────────────────────────────────┘  │
└─────────────────────────────────────────────────────────────┘

1.5.2 React Component Structure

App
├── Header
│   ├── Logo
│   ├── ConnectionStatus
│   └── SettingsButton
│
├── Sidebar
│   ├── BookmarkList
│   │   └── BookmarkItem (clickable)
│   └── RecentServers
│
├── MainContent
│   ├── ConnectForm (when no connection)
│   │   ├── AddressInput
│   │   ├── NicknameInput
│   │   ├── PasswordInput
│   │   └── ConnectButton
│   │
│   └── ChatView (when connected)
│       ├── ChannelTree
│       ├── ClientList
│       ├── MessageList
│       └── MessageInput
│
└── StatusBar
    ├── ConnectionInfo
    ├── AudioStatus
    └── LatencyDisplay

2. Algorithm Specifications

2.1 Shared Secret Computation (Old Protocol <3.1)

Input:  alpha[10], beta[10], shared_data[32]
Output: SharedIV[64], SharedMac[8]

1. SharedIV[0..20] = SHA-1(shared_data)
2. SharedIV[0..10] ^= alpha[0..10]
3. SharedIV[10..20] ^= beta[0..10]
4. SharedMac[0..8] = SHA-1(SharedIV)[0..8]

2.2 Shared Secret Computation (New Protocol ≥3.1)

Input:  alpha[10], beta[54], shared_data[32]
Output: SharedIV[64], SharedMac[8]

1. SharedIV[0..64] = SHA-512(shared_data)
2. SharedIV[0..10] ^= alpha[0..10]
3. SharedIV[10..64] ^= beta[0..54]
4. SharedMac[0..8] = SHA-1(SharedIV)[0..8]

2.3 Hash Cash Level Computation

Input:  omega (public key string), offset (u64)
Output: level (u8)

1. data = SHA-1(omega + offset.to_string())
2. level = 0
3. for byte in data:
4.     if byte == 0:
5.         level += 8
6.     else:
7.         level += trailing_zeros(byte)
8.         break
9. return level

2.4 UID Computation

Input:  publicKey (ASN.1-DER encoded)
Output: uid (base64 string)

1. hash = SHA-1(publicKey)
2. uid = base64(hash)

3. Interface Specifications

3.1 Tauri IPC Interface

// TypeScript interface for Tauri commands
interface ITauriCommands {
    // Identity management
    get_identities(): Promise<IdentityInfo[]>;
    create_identity(name: string): Promise<IdentityInfo>;
    delete_identity(id: string): Promise<void>;

    // Bookmark management
    get_bookmarks(): Promise<BookmarkInfo[]>;
    create_bookmark(name: string, address: string, port: number, 
                    nickname?: string): Promise<BookmarkInfo>;
    delete_bookmark(id: string): Promise<void>;

    // Connection
    connect(address: string, port: number, nickname: string, 
            password?: string): Promise<void>;
    disconnect(): Promise<void>;

    // Messaging
    send_message(target: string, message: string): Promise<void>;
    get_messages(server_address: string, limit: number, 
                 offset: number): Promise<MessageInfo[]>;
}

interface IdentityInfo {
    id: string;
    name: string;
    counter: number;
    max_counter: number;
}

interface BookmarkInfo {
    id: string;
    name: string;
    address: string;
    port: number;
    nickname: string | null;
    auto_connect: boolean;
    last_connected: string | null;
}

interface MessageInfo {
    id: number;
    invoker_name: string;
    message: string;
    timestamp: string;
    is_read: boolean;
}

3.2 Internal Rust Interfaces

// Protocol layer
pub trait PacketProcessor {
    fn encode(&self, packet: OutPacket) -> Result<Vec<OutUdpPacket>>;
    fn decode(&self, data: &[u8]) -> Result<InPacket>;
}

// Audio layer
pub trait AudioCapture {
    async fn start(&mut self) -> AudioResult<()>;
    async fn stop(&mut self) -> AudioResult<()>;
    async fn capture(&mut self) -> AudioResult<AudioFrame>;
}

pub trait AudioPlayback {
    async fn start(&mut self) -> AudioResult<()>;
    async fn stop(&mut self) -> AudioResult<()>;
    async fn play(&mut self, frame: AudioFrame) -> AudioResult<()>;
}

// Database layer
pub trait IdentityStore {
    fn create(&self, name: &str, key: &str) -> DatabaseResult<Identity>;
    fn get(&self, id: &str) -> DatabaseResult<Identity>;
    fn list(&self) -> DatabaseResult<Vec<Identity>>;
    fn update(&self, id: &str, updates: IdentityUpdates) -> DatabaseResult<()>;
    fn delete(&self, id: &str) -> DatabaseResult<()>;
}

4. Data Dictionary

4.1 Protocol Fields

Field Type Size Description
MAC [u8; 8] 8 bytes EAX message authentication code
PId u16 2 bytes Packet sequence ID
CId u16 2 bytes Client ID (C2S only)
PT u8 1 byte Packet type + flags
VId u16 2 bytes Voice packet ID
Codec u8 1 byte Audio codec type

4.2 Flag Bits

Bit Name Mask Description
7 UE 0x80 Unencrypted
6 CP 0x40 Compressed (QuickLZ)
5 NP 0x20 New protocol
4 FR 0x10 Fragmented
3-0 Type 0x0F Packet type (0-8)

4.3 Error Codes

Code Name Description
0x0000 ok Success
0x0200 client_invalid_id Invalid client ID
0x0201 client_nickname_inuse Nickname already in use
0x0208 client_invalid_password Wrong password
0x0300 channel_invalid_id Invalid channel ID
0x0400 server_invalid_id Invalid server ID
0x0403 server_maxclients_reached Server full
0x0701 connection_lost Connection lost

5. Test Design

5.1 Test Cases (32 total)

Protocol Tests (14)

  1. test_packet_type_conversion - PacketType enum conversion
  2. test_flags - Flag bit manipulation
  3. test_header_c2s - C2S header parsing
  4. test_header_s2c - S2C header parsing
  5. test_in_packet_parse - Input packet parsing
  6. test_out_packet - Output packet creation
  7. test_command_parse - Command string parsing
  8. test_command_serialize - Command serialization
  9. test_command_builder - CommandBuilder pattern
  10. test_escape_sequences - Escape/unescape
  11. test_init_packet_parse - Init packet parsing
  12. test_init_packet_serialize - Init packet serialization
  13. test_ack_packet - Acknowledgement packet
  14. test_packet_type_properties - Type property queries

Crypto Tests (12)

  1. test_sha1 - SHA-1 hash
  2. test_sha256 - SHA-256 hash
  3. test_sha512 - SHA-512 hash
  4. test_hash_password - Password hashing
  5. test_create_key_nonce - Key derivation
  6. test_create_encryption_key - Packet-specific key
  7. test_shared_secret_old - Old protocol shared secret
  8. test_shared_secret_new - New protocol shared secret
  9. test_key_cache - Key caching
  10. test_eax_encrypt_decrypt - EAX encryption/decryption
  11. test_fake_encrypt_decrypt - Fake encryption
  12. test_hash_cash_level - Hash Cash computation
  13. test_compute_uid - UID computation

Connection Tests (6)

  1. test_encode_version - Version encoding
  2. test_rsa_puzzle - RSA puzzle solver
  3. test_resend_manager - Retransmission manager
  4. test_rtt_estimator - RTT estimation
  5. test_sent_packet_retry - Packet retry logic

6. References

  1. TS3 Protocol Paper: refercence/tsdeclarations/ts3protocol.md
  2. Packet Definitions: refercence/tsdeclarations/Packets.txt
  3. Message Definitions: refercence/tsdeclarations/Messages.toml
  4. Source Code: src/ (4780 lines, 39 Rust files)