diff --git a/rollup.config.js b/rollup.config.js index de21456..983784c 100644 --- a/rollup.config.js +++ b/rollup.config.js @@ -1,5 +1,5 @@ export default { - input: 'lib/sturdy.js', + input: 'lib/sturdy-demo.js', output: { file: 'index.js', format: 'umd', diff --git a/src/sturdy-demo.ts b/src/sturdy-demo.ts new file mode 100644 index 0000000..09f309d --- /dev/null +++ b/src/sturdy-demo.ts @@ -0,0 +1,30 @@ +import { newKey } from './cryptography.js'; +import { attenuate, KEY_LENGTH, mint, Rewrite, sturdyEncode, validate } from './sturdy.js'; +import * as RW from './rewrite.js'; +import { Bytes, Dictionary } from 'preserves'; + +async function main() { + const m1 = await mint('hello world', new Bytes(KEY_LENGTH)); + console.log(m1.asPreservesText()); + const m2 = await attenuate(m1, Rewrite(RW.PBind('a', + RW.PCompound(RW.CRec(Symbol.for('says'), 2), + new Dictionary([ + [0, RW.Lit('Tony')]]))), + RW.TRef('a'))); + console.log(m2.asPreservesText()); + console.log('should be true:', await validate(m1, new Bytes(KEY_LENGTH))); + console.log('should be true:', await validate(m2, new Bytes(KEY_LENGTH))); + console.log('should be false:', await validate(m2, Bytes.of(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1))); + m2[0] = 'hello world2'; + console.log(m2.asPreservesText()); + console.log('should be false:', await validate(m2, new Bytes(KEY_LENGTH))); + m2[0] = 'hello world'; + console.log(m2.asPreservesText()); + console.log('should be true:', await validate(m2, new Bytes(KEY_LENGTH))); + console.log('should be false:', await validate(m2, await newKey())); + console.log((await newKey()).asPreservesText()); + console.log((await newKey()).asPreservesText()); + console.log(sturdyEncode(m2).asPreservesText()); +} + +main(); diff --git a/src/sturdy.ts b/src/sturdy.ts index b1fc19a..73b9659 100644 --- a/src/sturdy.ts +++ b/src/sturdy.ts @@ -6,10 +6,9 @@ // In Network and Distributed System Security Symposium. San Diego, // California: Internet Society, 2014. -import { mac, newKey } from './cryptography.js'; -import { Bytes, Dictionary, encode, is, Record, Value } from 'preserves'; +import { mac } from './cryptography.js'; +import { Bytes, encode, is, Record, Value } from 'preserves'; import type { Pattern, Template } from './rewrite.js'; -import * as RW from './rewrite.js'; export type EmbeddedRef = { ref: SturdyRef }; export type SturdyValue = Value; @@ -17,12 +16,11 @@ 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 - attenuations: Attenuation[], // caveats/rewrites, evaluated RIGHT-TO-LEFT - sig: Bytes, // *keyed* signature of canonicalEncode of rightmost item in [oid, ... attenuations] -}, EmbeddedRef>()(_SturdyRef, ['oid', 'attenuations', 'sig']); -export type SturdyRef = [SturdyValue, Attenuation[], Bytes] & { label: typeof _SturdyRef }; - -export type Attenuation = Array; // chain, evaluated left-to-right + 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 @@ -54,43 +52,17 @@ export async function mint(oid: SturdyValue, secretKey: Bytes): Promise { +export async function attenuate(r: SturdyRef, ... a: Array): Promise { return SturdyRef( SturdyRef._.oid(r), - [... SturdyRef._.attenuations(r), a], + [... SturdyRef._.caveatChain(r), a], await mac(SturdyRef._.sig(r), sturdyEncode(a)) ); } export async function validate(r: SturdyRef, secretKey: Bytes): Promise { - const sig = await SturdyRef._.attenuations(r).reduce( + 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)); } - -async function main() { - const m1 = await mint('hello world', new Bytes(KEY_LENGTH)); - console.log(m1.asPreservesText()); - const m2 = await attenuate(m1, Rewrite(RW.PBind('a', - RW.PCompound(RW.CRec(Symbol.for('says'), 2), - new Dictionary([ - [0, RW.Lit('Tony')]]))), - RW.TRef('a'))); - console.log(m2.asPreservesText()); - console.log('should be true:', await validate(m1, new Bytes(KEY_LENGTH))); - console.log('should be true:', await validate(m2, new Bytes(KEY_LENGTH))); - console.log('should be false:', await validate(m2, Bytes.of(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1))); - m2[0] = 'hello world2'; - console.log(m2.asPreservesText()); - console.log('should be false:', await validate(m2, new Bytes(KEY_LENGTH))); - m2[0] = 'hello world'; - console.log(m2.asPreservesText()); - console.log('should be true:', await validate(m2, new Bytes(KEY_LENGTH))); - console.log('should be false:', await validate(m2, await newKey())); - console.log((await newKey()).asPreservesText()); - console.log((await newKey()).asPreservesText()); - console.log(sturdyEncode(m2).asPreservesText()); -} - -main();