Concept
ASK — Asymmetric Shared Keys
ASK stands for Asymmetric Shared Key. The platform issues two complementary halves — one to each party — so that anyone with a HexaEight identity can encrypt for any other HexaEight identity without prior key exchange.
Inversion of classical PKI
In RSA / ECC, Alice needs Bob's public key before she can encrypt for him. Bob has to have generated a keypair, published the public half, and Alice has to trust the channel she got it over (hence PKI, certificates, OAuth, JWK endpoints).
HexaEight inverts this. Alice asks the platform for "the ASK that lets me talk to Bob." She gets a key. Bob, when he eventually wants to decrypt, asks the platform for "the ASK that lets me read messages from Alice." He gets a complementary key — paired with Alice's but different. Neither side ever sees the other's password.
Why the platform can't decrypt
The platform combines each party's password (which it does not store cleartext and cannot reverse) with SHAKE-256-derived key material. The output is two halves of a shared secret. The platform sees the derivation inputs but never the underlying password, and SHAKE-256 is one-way.
Key Generation Time (KGT)
Every ASK is bound to a 15-minute window — the KGT. See KGT for details. KGT serves two purposes:
- Rotation: A leaked ASK is only useful for messages within its 15-minute window.
- Clock skew tolerance: Two parties whose clocks differ by less than 15 minutes compute the same KGT.
The ASK lifecycle in code
// First time talking to Bob — Bridge fetches the ASK from the platform
string ask = await client.FetchAskAsync("bob.example.com", kgt);
// Cache test (no fetch)
bool warm = client.HasCachedAsk("bob.example.com", kgt);
// Pin so it survives auto-eviction
client.PinAsk("bob.example.com", kgt, ask);
// Pin under a session for fast Variant B lookup
string sessionId = Guid.NewGuid().ToString();
client.PinAskForSession(sessionId, ask);
// Save to disk so the next process gets a warm cache
await client.SaveAskCacheToDiskAsync("./cache.json");
// On next startup
await client.LoadAskCacheFromDiskAsync("./cache.json");
// Or hand it to auto-persist (2-second debounced writes)
await client.EnableAutoPersistAsync("./cache.json", loadIfExists: true); Cache shape
The ASK cache is indexed two ways:
- By
(recipient, kgt)— for direct peer lookups. - By
sha256(sessionId)— for Sessioned envelopes.
Pinning marks an entry as preferred so cache pressure can't evict it.
ClearAskCache() wipes everything.
Common patterns
Pre-warm for known peers
foreach (var peer in knownPeers)
{
string ask = await client.FetchAskAsync(peer, currentKgt);
client.PinAsk(peer, currentKgt, ask);
}
await client.SaveAskCacheToDiskAsync("./warm.json"); Long-lived session
// Alice (sender side)
string sessionId = Guid.NewGuid().ToString(); // share with Bob out-of-band
string ask = await alice.FetchAskAsync("bob.example.com", currentKgt);
alice.PinAskForSession(sessionId, ask);
// Every encrypted message travels as a Variant B envelope
var envelope = await alice.EncryptEnvelopeAsync(
recipient: "bob.example.com",
body: "follow-up",
sessionId: sessionId); See also
- Envelopes — how an ASK becomes a wire format.
- KGT — the time window scoping every ASK.
- Bridge API —
FetchAskAsync,PinAsk,SaveAskCacheToDiskAsync,EnableAutoPersistAsync.