syndicate-js/packages/core/src/transport/sturdy.ts

73 lines
2.5 KiB
TypeScript

/// SPDX-License-Identifier: GPL-3.0-or-later
/// SPDX-FileCopyrightText: Copyright © 2016-2023 Tony Garnock-Jones <tonyg@leastfixedpoint.com>
// 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, decode, encode, is, neverEmbeddedType, Value } from '@preserves/core';
import * as S from '../gen/sturdy.js';
export * from '../gen/sturdy.js';
export type SturdyValue = Value<S._embedded>;
export const KEY_LENGTH = 16; // 128 bits
export function embeddedNotAllowed(): never {
throw new Error("Embedded Ref not permitted in SturdyRef");
}
export function sturdyEncode(v: SturdyValue): Bytes {
return encode<S._embedded>(v, {
canonical: true,
includeAnnotations: false,
embeddedEncode: neverEmbeddedType,
});
}
export function sturdyDecode(bs: Bytes): SturdyValue {
return decode<S._embedded>(bs, {
includeAnnotations: false,
embeddedDecode: neverEmbeddedType,
});
}
export async function mint(oid: SturdyValue, secretKey: Bytes): Promise<S.SturdyRef> {
return S.SturdyRef(S.Parameters({
oid,
sig: await mac(secretKey, sturdyEncode(oid)),
caveats: S.CaveatsField.absent(),
}));
}
async function chainMac(key: Bytes | Promise<Bytes>, caveats: S.Caveat[]): Promise<Bytes> {
return caveats.reduce(async (key, c) => mac(await key, sturdyEncode(S.fromCaveat(c))), key);
}
export function caveatChain(r: S.SturdyRef): S.Caveat[] {
switch (r.parameters.caveats._variant) {
case "present": return r.parameters.caveats.caveats;
case "absent": return [];
case "invalid": throw new Error("Invalid caveats on sturdyref");
}
}
export async function attenuate(r: S.SturdyRef, ... a: S.Caveat[]): Promise<S.SturdyRef> {
if (a.length === 0) return r;
return S.SturdyRef(S.Parameters({
oid: r.parameters.oid,
caveats: S.CaveatsField.present([... caveatChain(r), ... a]),
sig: await chainMac(r.parameters.sig, a),
}));
}
export async function validate(r: S.SturdyRef, secretKey: Bytes): Promise<boolean> {
const sig = await chainMac(await mac(secretKey, sturdyEncode(r.parameters.oid)), caveatChain(r));
return is(sig, r.parameters.sig);
}