∪ Audiences

Who can open a value.

An audience is the set of identities that can read a value. It is not a list checked at the door: it is the set of keys that can decrypt it.

Kernel: this.me 4.2.1 (npm) ⓘ

Theory: Encrypted Audiences and Cryptographic Set-Chemistry. This page is language-neutral; each implementation is linked at the end.

Three operations

Operation Opens for How
OR a ∪ b any one member one content key, wrapped once per member
AND a ∩ b only the whole coalition the content key split into XOR shares, one per member
minus A \ x everyone in A except x resolved at seal time; revoking later means resealing with a new key
K = s₁ ⊕ s₂ ⊕ … ⊕ sₙ        AND: every share needed
W_i = wrap(pub_i, K)          OR: any one wrap opens K

There is no NOT and no threshold-k (Shamir). A key, once read, cannot be un-read: removal works forward, by resealing.

The smallest audience: {self}

A secret scope is an audience of one. The owner reads; a guest gets nothing.

import { ME } from "this.me";
const me = new ME() as any;

me.wallet["_"]("vault-key");
me.wallet.note("private");
me("wallet.note")              // "private"
me.as(null)("wallet.note")     // undefined (guest)

OR from npm 4.2.1

The npm package exports the wrapping primitive, so an OR audience can be built by hand: wrap one content key for each member.

const a = await ME.generateP256KeyPair();
const b = await ME.generateP256KeyPair();
const out = await ME.generateP256KeyPair();
const S = "content-key-123";

const Wa = await ME.wrapSecretV1({ secret: S, recipientPublicKey: a.publicKey, kid: "a", class: "identity-key" });
const Wb = await ME.wrapSecretV1({ secret: S, recipientPublicKey: b.publicKey, kid: "b", class: "identity-key" });

await ME.unwrapSecretV1(Wa, a.privateKey, "utf8");    // "content-key-123"
await ME.unwrapSecretV1(Wb, b.privateKey, "utf8");    // "content-key-123"
await ME.unwrapSecretV1(Wa, out.privateKey, "utf8");  // throws OperationError

Run on Node with npm this.me 4.2.1; the outputs above are the real ones. Each wrap is { version, class, kid, publicKey, encryption, policy }. P-256 key pairs are generated randomly, not derived from the seed. Getting each member’s public key to the sender is not solved by the kernel.

Live: choose the audience

Click a circle to add or remove that identity. Each change wraps a fresh content key K for every member with ME.wrapSecretV1, then every party tries ME.unwrapSecretV1, here, on this.me 4.2.1 from npm. Members get K; the others get the kernel’s real error.

AND and minus

Not exported by npm 4.2.1, so not run here. They live in the 4.2.1 source, audience.ts, tested by audience-algebra.test.ts (16/16).

anabobK = s_ana ⊕ s_boball shares neededanabobeve{ana, bob, eve} \ eve
AND: each member wraps only a share; all of them together rebuild K (splitXor). Minus: drops every coalition containing the member, once, when sealing (minus L101–107); seal and open: L207–247.

Status by implementation

  OR / AND / minus Wrapped secret (one recipient)
TypeScript this.me 4.2.1 implemented and tested in the source; not exported by the npm package exported (ME.wrapSecretV1, ME.unwrapSecretV1)
Rust rust-v0.3.3 none implemented and tested
Python no implementation yet no implementation yet

The sealed format is an envelope tree, { version, audience, epoch, ciphertext, tree }, whose nodes are leaf (one wrap), or and and.

Implementations

Python: no implementation yet.

See also