Rust port of OmniVox, wire-compatible with the C++ original

Byte-exact cereal binary layout, RFC 8439 ChaCha20-Poly1305, RFC 7914
scrypt defaults; tokio transport preserving the original's observable
behavior (contract B1-B3, B5 quirks included). Parity evidence: golden
C++ vectors in tests/vectors.rs and loopback behavior tests; both
cross-directions verified against the unmodified C++ binaries.

Source: https://git.0xfeedc0de.com/michael/OmniVox
This commit is contained in:
2026-09-30 06:35:24 -04:00
commit 268b986bb9
19 changed files with 3042 additions and 0 deletions
+11
View File
@@ -0,0 +1,11 @@
Provenance of the golden vectors in tests/vectors.txt.
Generate with (from a checkout of the original C++ OmniVox tree, which provides common.h, include/, cereal/, asio/, cryptopp/; requires libcryptopp.a and system fmt):
g++ -std=c++20 -O1 -DDEBUG -I<cpp-root> -I<cpp-root>/include \
-I<cpp-root>/cereal/include -I<cpp-root>/asio/include -I<cpp-root>/cryptopp \
$(pkg-config --cflags fmt) reference/cppgen.cpp \
<cpp-root>/cryptopp/libcryptopp.a $(pkg-config --libs fmt) -lpthread
The scrypt derivations take ~1 s and 128 MiB each. Any change to these
bytes means the Rust port has regressed against the original.
+73
View File
@@ -0,0 +1,73 @@
// Golden-vector generator for the Rust port. Output is normative.
#include "common.h"
#include <iomanip>
#include <sstream>
static std::string hex(const uint8_t* p, size_t n) {
std::ostringstream os; os << std::hex << std::setfill('0');
for (size_t i = 0; i < n; i++) os << std::setw(2) << (int)p[i];
return os.str();
}
static std::string hexv(const std::vector<uint8_t>& v) { return hex(v.data(), v.size()); }
int main() {
// 1. scrypt key derivation (default params N=2^17 r=8 p=1)
OmniVox<DatagramPayload> vox;
std::vector<uint8_t> pw(g_sPassword.begin(), g_sPassword.end());
auto key = vox.deriveEncryptKey(pw, g_vecSalt);
std::cout << "scrypt_key=" << hex(key.data(), key.size()) << "\n";
// extra scrypt vector: known short inputs
std::vector<uint8_t> pw2{'p','a','s','s'}, salt2{'s','a','l','t'};
auto key2 = vox.deriveEncryptKey(pw2, salt2);
std::cout << "scrypt_key2=" << hex(key2.data(), key2.size()) << "\n";
vox.setEncryptKey(key);
// 2. cereal byte layouts
DatagramPayload dp;
dp.id = 0x1234; dp.v1.qword = 0x1122334455667788ULL;
dp.payload = {'H','e','l','l','o'};
std::cout << "cereal_datagram=" << hexv(serializeObject(dp)) << "\n";
DatagramPayload dp0; // all defaults
std::cout << "cereal_datagram_default=" << hexv(serializeObject(dp0)) << "\n";
AeadPacket ap{};
for (int i = 0; i < 12; i++) ap.nonce[i] = (uint8_t)i;
for (int i = 0; i < 16; i++) ap.tag[i] = (uint8_t)(0xA0 + i);
ap.payload = {0xDE, 0xAD, 0xBE, 0xEF};
std::cout << "cereal_aead=" << hexv(serializeObject(ap)) << "\n";
AeadPacket ap0{};
std::cout << "cereal_aead_empty=" << hexv(serializeObject(ap0)) << "\n";
// 3. AEAD vectors: fixed nonce, CryptoPP as the oracle
auto vec = [](std::string s){ return std::vector<uint8_t>(s.begin(), s.end()); };
{
auto pkt = vox.encrypt(vec("Hello")); // random nonce: structure check only
std::cout << "aead_random_payloadlen=" << pkt.payload.size() << "\n";
}
// deterministic: set key, encrypt with forced nonce by round-tripping through
// a fresh object whose rng we can't seed... instead use Crypto++ directly.
{
CryptoPP::ChaCha20Poly1305::Encryption enc;
uint8_t nonce[12] = {0,1,2,3,4,5,6,7,8,9,10,11};
enc.SetKeyWithIV(key.data(), key.size(), nonce, 12);
std::string pt = "Hello";
std::vector<uint8_t> ct(pt.size()), tag(16);
enc.EncryptAndAuthenticate(ct.data(), tag.data(), 16, nonce, 12,
nullptr, 0,
(const unsigned char*)pt.data(), pt.size());
std::cout << "aead_ct=" << hexv(ct) << "\n";
std::cout << "aead_tag=" << hex(tag.data(), 16) << "\n";
}
{
CryptoPP::ChaCha20Poly1305::Encryption enc;
uint8_t nonce[12] = {0,1,2,3,4,5,6,7,8,9,10,11};
enc.SetKeyWithIV(key.data(), key.size(), nonce, 12);
std::vector<uint8_t> ct(0), tag(16);
enc.EncryptAndAuthenticate(ct.data(), tag.data(), 16, nonce, 12,
nullptr, 0, (const unsigned char*)"", 0);
std::cout << "aead_empty_tag=" << hex(tag.data(), 16) << "\n";
}
return 0;
}
+10
View File
@@ -0,0 +1,10 @@
scrypt_key=f64f4c05eb28676be5e4dd760c7f9409327a47e11abb66adc593b2bd039a0650
scrypt_key2=f21278eee02abf1ad982428b5ab5d1ffccb62f6f1163dc616bfe30686e143ea0
cereal_datagram=34128877665544332211050000000000000048656c6c6f
cereal_datagram_default=000000000000000000000000000000000000
cereal_aead=000102030405060708090a0ba0a1a2a3a4a5a6a7a8a9aaabacadaeaf0400000000000000deadbeef
cereal_aead_empty=000000000000000000000000000000000000000000000000000000000000000000000000
aead_random_payloadlen=5
aead_ct=3e3c330d3b
aead_tag=89c6b5d2dbc9d3f7c82c12825f992c49
aead_empty_tag=791eac80a87ddffcf45d6bad2769ea04