Skip to content

Commit

Permalink
wip
Browse files Browse the repository at this point in the history
  • Loading branch information
nichoth committed Apr 29, 2024
1 parent 2585853 commit 46f2e69
Show file tree
Hide file tree
Showing 3 changed files with 145 additions and 6 deletions.
91 changes: 90 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,92 @@
# ailuropoda

Implementing bamboo.
Implementing [bamboo](https://github.com/AljoschaMeyer/bamboo), using only browser compatible cryptography.

## install

```sh
npm i -S @bicycle-codes/ailuropoda
```

## use

```js
import {
create as createMsg,
SignedPost,
lipmaaLink,
createBatch,
getLipmaaPath,
isValid,
verifyLipmaas
} from '@bicycle-codes/ailuropoda'
```

## example

Use the function `createBatch` to create a list with lipmaa links.

See [the diagram](https://github.com/AljoschaMeyer/bamboo?tab=readme-ov-file#links-and-entry-verification) of the list structure.

```ts
import { Identity, create as createID } from '@bicycle-codes/identity'
import { createCryptoComponent } from '@ssc-half-light/node-components'
import { createBatch } from '@bicycle-codes/ailuropoda'

const alicesCrytpo = await createCryptoComponent()
const alice = await createID(alicesCrytpo, {
humanName: 'alice',
humanReadableDeviceName: 'computer'
})

const newMsgs = [
{ content: { text: 'hello 1' } },
{ content: { text: 'hello 2' } },
{ content: { text: 'hello 3' } },
{ content: { text: 'hello 4' } },
{ content: { text: 'hello 5' } }
]

const list = await createBatch(alice, alicesCrytpo, {
// we are just using an in-memory array of messages
getKeyFromIndex: async (i:number, msgs:SignedPost[]) => {
const msg = msgs[i]
if (!msg) return null
return msg.metadata.key
}
}, newMsgs) // pass in a list with message content
```

## API

### create
Create a message.

```ts
async function create (
user:Identity,
crypto:Implementation,
opts:{
content:Content,
limpaalink?:string|null, // <-- the key of the lipmaa message
seq:number,
prev:SignedPost|null|undefined,
}
):Promise<SignedPost>
```

```js
import { create as createMsg } from '@bicycle-codes/ailuropoda'
const post = await createMsg(
alice,
alicesCrytpo,
{
seq: 1,
prev: null,
content: {
text: 'hello'
}
}
)
```
32 changes: 29 additions & 3 deletions src/index.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,10 @@
import ts from 'monotonic-timestamp'
import type { Implementation } from '@oddjs/odd/components/crypto/implementation'
import { SignedMessage } from '@bicycle-codes/message'
// import { createDebug } from '@nichoth/debug'
import { blake3 } from '@noble/hashes/blake3'
import { DID, Identity, sign, verifyFromString } from '@bicycle-codes/identity'
import { toString } from 'uint8arrays/to-string'
import stringify from 'json-canon'
// type KeyStore = Implementation['keystore']
// const debug = createDebug()

export interface Metadata {
timestamp:number;
Expand Down Expand Up @@ -85,6 +82,35 @@ export async function isValid (msg:SignedPost):Promise<boolean> {
return isOk
}

export async function verifyLipmaas (list:SignedPost[], {
messageFromKey
}:{
messageFromKey:(key:string)=>Promise<SignedPost>
}, msg:SignedPost, path?:number[]):Promise<{ isOk: boolean, path:number[] }> {
// find the shortest path to the first message
// we are assuming the `seq` number in the message
// is +1 the message's index

path = (path || []).concat(msg.metadata.seq)

// check the message signature
const isOk = await isValid(msg)
if (!isOk) return { isOk: false, path }

// we are at the first message
if (!msg.metadata.lipmaalink) {
return {
// be sure there is not an invalid sequence
isOk: isOk && msg.metadata.seq <= 1,
path
}
}

const next = await messageFromKey(msg.metadata.lipmaalink)

return await verifyLipmaas(list, { messageFromKey }, next, path)
}

/**
* Create a linked list of messages.
*/
Expand Down
28 changes: 26 additions & 2 deletions test/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,8 @@ import {
lipmaaLink,
createBatch,
getLipmaaPath,
isValid
isValid,
verifyLipmaas
} from '../src/index.js'

let alice:Identity
Expand Down Expand Up @@ -72,6 +73,7 @@ async function getKey (i:number, msgs:SignedPost[]):Promise<string|null> {
}

let list:SignedPost[]
let list2:SignedPost[]
test('create a linked list', async t => {
const newMsgs = [
{ content: { text: 'hello 1' } },
Expand All @@ -95,7 +97,7 @@ test('create a linked list', async t => {
})

// create a list with 40 items
const list2 = await createBatch(alice, alicesCrytpo, {
list2 = await createBatch(alice, alicesCrytpo, {
getKeyFromIndex: getKey
}, arr)

Expand Down Expand Up @@ -126,6 +128,28 @@ test('verify messages', async t => {
t.ok(!notOk, 'should not verify an invalid signature')
})

async function messageFromKey (key) {
return list2.find(post => {
return post.metadata.key === key
}) as SignedPost
}

test('verify lipmaa links', async t => {
const { isOk, path } = await verifyLipmaas(list2, {
messageFromKey
}, list2[39])

t.ok(isOk, 'should verify message 40')
t.deepEqual(path, [40, 13, 4, 1], 'should return the expected path')

const { isOk: isOk2, path: path2 } = await verifyLipmaas(list2, {
messageFromKey
}, list2[24])
t.ok(isOk2, 'should verify message 26')

console.log('path2', path2)
})

function expectedLipmas () {
return [
{ lipmaa: 0, n: 0 },
Expand Down

0 comments on commit 46f2e69

Please sign in to comment.