How-to

Verify the original sender

Applies to: .NET SDK Last updated: 2026-06-24

Goal

Confirm who actually originated a relayed message, and decide what stronger guarantees you want on top of what the crypto itself proves.

What the crypto proves

When the final recipient calls Client.MultiDecryptMessageUsingSharedKeyAsync and gets a non-empty plaintext, that plaintext envelope's SENDER and RECEIVER fields are cryptographically authentic — the multi-shared key was derived for that exact (sender, chain) tuple at encrypt time, and a substituted intermediate or wrong final recipient would fail the peel chain. So you do not need additional trust to believe the SENDER field on a successfully decrypted multi-party message.

// On the final recipient
string aliceAsk = await alice.FetchDestinationAskAsync(
    logintoken: aliceToken, kgt, destination: "~D~" + claimedOriginator);

string plaintextEnvelope = await alice.MultiDecryptMessageUsingSharedKeyAsync(
    innerCiphertext, aliceAsk);

if (string.IsNullOrEmpty(plaintextEnvelope)) {
    // Decrypt failed — either claimedOriginator is wrong, a hop was skipped,
    // or the ciphertext was tampered with. Reject.
    return;
}

using var doc = JsonDocument.Parse(plaintextEnvelope);
string sender   = doc.RootElement.GetProperty("SENDER").GetString()   ?? "";
string receiver = doc.RootElement.GetProperty("RECEIVER").GetString() ?? "";
// sender == claimedOriginator now is guaranteed (decrypt would have failed otherwise)
ProcessMessage(sender, receiver, doc.RootElement.GetProperty("BODY").GetString());

Pattern — non-repudiation via content signature

The decrypted SENDER tells you who originated the message at this kgt window, authenticated by the platform-derived multi-shared key. For non-repudiable, transport-independent proof that lasts beyond the kgt window (audit logs, legal evidence), have the originator sign the body before encrypting:

// On the originator (functions involved):
//   signingClient.SignContent(body)
//   Client.FetchDestinationAskAsync(logintoken, kgt, chain)   // multi-shared key
//   Client.MultiEncryptMessageUsingSharedKeyAsync(chain, signed, multiAsk)
string signed = signingClient.SignContent(plaintextBody);
string multiAsk = await origin.FetchDestinationAskAsync(originToken, kgt, chain);
string ct = await origin.MultiEncryptMessageUsingSharedKeyAsync(chain, signed, multiAsk);
// On the final recipient (after MultiDecrypt):
bool isAuthentic = signingClient.VerifySignature(
    body: extractedBody, expectedSigner: sender);

if (!isAuthentic) { /* tampered or wrong signer */ return; }

See also