Research
Security News
Malicious npm Packages Inject SSH Backdoors via Typosquatted Libraries
Socket’s threat research team has detected six malicious npm packages typosquatting popular libraries to insert SSH backdoors.
@bicycle-codes/ailuropoda
Advanced tools
Implementing bamboo, using only browser compatible cryptography.
Ailuropoda is the science name for a panda.
npm i -S @bicycle-codes/ailuropoda
Import types and functions.
import {
create as createMsg,
SignedPost,
lipmaaLink,
createBatch,
getLipmaaPath,
isValid,
verifyLipmaas
} from '@bicycle-codes/ailuropoda'
Log entries are { metadata, content }
, where metadata is
a signed object like below.
interface Metadata {
timestamp:number;
proof:string,
key:string, // <-- base64url encoded
seq:number;
lipmaalink:string|null;
prev:string|null;
username:string;
author:DID;
}
import { SignedMessage } from '@bicycle-codes/message'
type SignedMetadata = SignedMessage<Metadata>
export interface Content {
text:string,
alt?:string[],
mentions?:string[]
}
type SignedPost = { metadata:SignedMetadata, content:Content }
Use the function createBatch
to create a list with lipmaa links.
See the diagram for a nice visualization of the list structure.
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
lipmaaLink (n)
Get the lipmaa number number given a sequence number.
function lipmaaLink (n:number):number
lipmaaLink
exampleconst lipmaas = ([...Array(41).keys()]).map(n => {
return { lipmaa: lipmaaLink(n), n }
})
lipmaas
is like this:
[
{ lipmaa: 0, n: 0 }, { lipmaa: 0, n: 1 },
{ lipmaa: 1, n: 2 }, { lipmaa: 2, n: 3 },
{ lipmaa: 1, n: 4 }, { lipmaa: 4, n: 5 },
{ lipmaa: 5, n: 6 }, { lipmaa: 6, n: 7 },
{ lipmaa: 4, n: 8 }, { lipmaa: 8, n: 9 },
{ lipmaa: 9, n: 10 }, { lipmaa: 10, n: 11 },
{ lipmaa: 8, n: 12 }, { lipmaa: 4, n: 13 },
{ lipmaa: 13, n: 14 }, { lipmaa: 14, n: 15 },
{ lipmaa: 15, n: 16 }, { lipmaa: 13, n: 17 },
{ lipmaa: 17, n: 18 }, { lipmaa: 18, n: 19 },
{ lipmaa: 19, n: 20 }, { lipmaa: 17, n: 21 },
{ lipmaa: 21, n: 22 }, { lipmaa: 22, n: 23 },
{ lipmaa: 23, n: 24 }, { lipmaa: 21, n: 25 },
{ lipmaa: 13, n: 26 }, { lipmaa: 26, n: 27 },
{ lipmaa: 27, n: 28 }, { lipmaa: 28, n: 29 },
{ lipmaa: 26, n: 30 }, { lipmaa: 30, n: 31 },
{ lipmaa: 31, n: 32 }, { lipmaa: 32, n: 33 },
{ lipmaa: 30, n: 34 }, { lipmaa: 34, n: 35 },
{ lipmaa: 35, n: 36 }, { lipmaa: 36, n: 37 },
{ lipmaa: 34, n: 38 }, { lipmaa: 26, n: 39 },
{ lipmaa: 13, n: 40 }
]
Note the lipmaa
vs n
properties match with this diagram.
create (user, crypto, opts)
Create a message. This does not deal with lipmaa links. You would need to pass them in.
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>
import { create as createMsg } from '@bicycle-codes/ailuropoda'
const post = await createMsg(
alice,
alicesCrytpo,
{
seq: 1,
prev: null,
content: {
text: 'hello'
}
}
)
isValid (message)
Verify a message. This does not look at links, only the signature and hash.
async function isValid (msg:SignedPost):Promise<boolean>
const { isOk } = await isValid(post)
// => true
verifyLipmaas ({ messageFromKey }, msg, path)
Check that all the messages between the given message and message number 1 are valid. This will use the shortest path from the given message to the first message.
async function verifyLipmaas ({
messageFromKey
}:{
messageFromKey:(key:string)=>Promise<SignedPost>
}, msg:SignedPost, path?:number[]):Promise<{
isOk: boolean,
path:number[]
}>
const { isOk, path } = await verifyLipmaas(list2, {
messageFromKey
}, list2[39]) // array is 0 indexed, so 39 is seq number 40
// isOk = true
// path = [40, 13, 4, 1]
getLipmaaPath (seq, prev)
Get the shortest path between the given sequence number and
the first message. The parameter prev
is used internally, for recusion.
function getLipmaaPath (seq:number, prev?:number[]):number[]
Return an array of sequence numbers, starting with the first:
[ 1, 4, 13 ]
createBatch (user, crypto, opts, messages)
Create a linked list of the given messages, with lipmaa links.
async function createBatch (
user:Identity,
crypto:Implementation,
opts: {
getKeyFromIndex:(i:number, msgs:SignedPost[]) => Promise<string|null>
},
msgs:{
content:Content,
seq?:number,
prev?:SignedPost|null|undefined,
}[],
_out?:SignedPost[]
):Promise<SignedPost[]>
createBatch
exampleCreate a linked list with in-memory content, starting from entry number 1.
Note in the example, getKey
is synchronous, but we need to return a
promise because that's what the API expects.
Takes a parameter getKeyFromIndex
that will return the key for an entry
given its index.
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, {
getKeyFromIndex: getKey
}, newMsgs)
async function getKey (i:number, msgs:SignedPost[]):Promise<string|null> {
const msg = msgs[i]
if (!msg) return null
return msg.metadata.key
}
append (user, crypto, opts)
Given a previous message and a function that will return a message by its sequence number, create a new message with the correct lipmaa key.
async function append (
user:Identity,
crypto:Implementation,
opts:{
getBySeq:(seq:number) => Promise<SignedPost>
content:Content,
prev:SignedPost
}
):Promise<SignedPost>
const list = await createBatch(alice, alicesCrytpo, {
getKeyFromIndex: async (i, msgs) => {
return msgs[i].metadata.key
},
}, msgs)
const newMsg = await append(alice, alicesCrytpo, {
getBySeq: async (seq) => {
return list[seq - 1] // 0 vs 1 indexed
},
prev: list[list.length - 1],
content: { text: 'hello 40' }
})
Generated via typescript.
FAQs
Bamboo implemented in typescript
We found that @bicycle-codes/ailuropoda demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 1 open source maintainer collaborating on the project.
Did you know?
Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.
Research
Security News
Socket’s threat research team has detected six malicious npm packages typosquatting popular libraries to insert SSH backdoors.
Security News
MITRE's 2024 CWE Top 25 highlights critical software vulnerabilities like XSS, SQL Injection, and CSRF, reflecting shifts due to a refined ranking methodology.
Security News
In this segment of the Risky Business podcast, Feross Aboukhadijeh and Patrick Gray discuss the challenges of tracking malware discovered in open source softare.