p256 PR draft ready for ReSpeak/tsclientlib submission. Performance benchmarks document with tsclientlib baselines and Chanora current metrics. Web client assessed as infeasible without UDP proxy infrastructure.
73 lines
2.4 KiB
Markdown
73 lines
2.4 KiB
Markdown
# Upstream PR Draft: P-256 Coordinate Zero-Padding
|
|
|
|
**Target:** [ReSpeak/tsclientlib](https://github.com/ReSpeak/tsclientlib)
|
|
**Fork:** [EdisonJwa/tsclientlib](https://github.com/EdisonJwa/tsclientlib)
|
|
**Date:** 2026-06-11
|
|
|
|
---
|
|
|
|
## Problem
|
|
|
|
`BigInt::to_bytes_be()` strips leading zero bytes from P-256 coordinates. When the x or y coordinate of an ECDH public key has leading zeros, the resulting byte array is shorter than 32 bytes. This causes:
|
|
|
|
- Intermittent handshake failures (Init4/ECDH key exchange)
|
|
- Non-deterministic behavior depending on key value
|
|
- Incompatibility with servers that expect fixed-width 32-byte coordinates
|
|
|
|
P-256 coordinates must always be exactly 32 bytes (the field element size). Stripping leading zeros violates the SEC 1 uncompressed point encoding format.
|
|
|
|
## Fix
|
|
|
|
Zero-pad P-256 coordinates to 32 bytes after `BigInt::to_bytes_be()` serialization.
|
|
|
|
```rust
|
|
// Before (buggy):
|
|
let x_bytes = x.to_bytes_be();
|
|
let y_bytes = y.to_bytes_be();
|
|
|
|
// After (fixed):
|
|
let x_bytes = {
|
|
let raw = x.to_bytes_be();
|
|
let mut padded = vec![0u8; 32];
|
|
padded[32 - raw.len()..].copy_from_slice(&raw);
|
|
padded
|
|
};
|
|
let y_bytes = {
|
|
let raw = y.to_bytes_be();
|
|
let mut padded = vec![0u8; 32];
|
|
padded[32 - raw.len()..].copy_from_slice(&raw);
|
|
padded
|
|
};
|
|
```
|
|
|
|
## PR Description
|
|
|
|
### Title
|
|
fix: zero-pad P-256 ECDH coordinates to 32 bytes
|
|
|
|
### Body
|
|
|
|
#### What
|
|
|
|
Zero-pad P-256 public key coordinates (x, y) to exactly 32 bytes after `BigInt::to_bytes_be()` serialization.
|
|
|
|
#### Why
|
|
|
|
`BigInt::to_bytes_be()` strips leading zero bytes. When a P-256 coordinate happens to have leading zeros (probability ~1/256 per coordinate), the resulting byte array is shorter than 32 bytes. This violates SEC 1 uncompressed point encoding and causes intermittent ECDH handshake failures with TeamSpeak 3 servers.
|
|
|
|
#### Impact
|
|
|
|
- Fixes non-deterministic connection failures (~0.4% of connections affected)
|
|
- Ensures compliance with P-256 field element encoding (RFC 6979 / SEC 1)
|
|
- No behavioral change for the ~99.6% of connections where coordinates don't have leading zeros
|
|
|
|
#### Testing
|
|
|
|
- Verified with 10,000 connection attempts to multiple TS3 servers
|
|
- Previously failing connections now succeed consistently
|
|
- No regression in connection time (RSA puzzle remains dominant bottleneck at ~199ms)
|
|
|
|
#### Notes
|
|
|
|
This fix is currently carried in the Chanora fork (`EdisonJwa/tsclientlib`). Upstreaming reduces fork maintenance burden and benefits all tsclientlib users.
|