// Basically Macaroons [1] in a Dataspace context // // [1]: Birgisson, Arnar, Joe Gibbs Politz, Úlfar Erlingsson, Ankur // Taly, Michael Vrable, and Mark Lentczner. “Macaroons: Cookies with // Contextual Caveats for Decentralized Authorization in the Cloud.” // In Network and Distributed System Security Symposium. San Diego, // California: Internet Society, 2014. import { mac } from './cryptography.js'; import { Bytes, encode, is, Record, Value } from 'preserves'; import type { Pattern, Template } from './rewrite.js'; export type EmbeddedRef = { ref: SturdyRef }; export type SturdyValue = Value; export const _SturdyRef = Symbol.for('sturdyref'); export const SturdyRef = Record.makeConstructor<{ oid: SturdyValue, // (arbitrary) name of the ultimate target of the ref caveatChain: Array[], // ^ caveats/rewrites. Evaluated RIGHT-TO-LEFT; each Array is evaluated LEFT-TO-RIGHT sig: Bytes, // *keyed* signature of canonicalEncode of rightmost item in [oid, ... caveatChain] }, EmbeddedRef>()(_SturdyRef, ['oid', 'caveatChain', 'sig']); export type SturdyRef = [SturdyValue, Array[], Bytes] & { label: typeof _SturdyRef }; export type Caveat = Or; // ^ embodies 1st-party caveats over assertion structure, but nothing else // can add 3rd-party caveats and richer predicates later export const _Or = Symbol.for('or'); export const Or = () => Record.makeConstructor<{ alternatives: T[] }, EmbeddedRef>()(_Or, ['alternatives']); export type Or = T | Record; export const _Rewrite = Symbol.for('rewrite'); export const Rewrite = Record.makeConstructor<{ pattern: Pattern, template: Template, }, EmbeddedRef>()(_Rewrite, ['pattern', 'template']); export type Rewrite = ReturnType; export const KEY_LENGTH = 16; // 128 bits export function sturdyEncode(v: SturdyValue): Bytes { return encode(v, { canonical: true, includeAnnotations: false, encodePointer(v) { return v }, }); } export async function mint(oid: SturdyValue, secretKey: Bytes): Promise { return SturdyRef(oid, [], await mac(secretKey, sturdyEncode(oid))); } export async function attenuate(r: SturdyRef, ... a: Array): Promise { return SturdyRef( SturdyRef._.oid(r), [... SturdyRef._.caveatChain(r), a], await mac(SturdyRef._.sig(r), sturdyEncode(a)) ); } export async function validate(r: SturdyRef, secretKey: Bytes): Promise { const sig = await SturdyRef._.caveatChain(r).reduce( async (sig, a) => mac(await sig, sturdyEncode(a)), mac(secretKey, sturdyEncode(SturdyRef._.oid(r)))); return is(sig, SturdyRef._.sig(r)); }