Skip to content

Instantly share code, notes, and snippets.

@b5
Created August 20, 2021 19:07
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save b5/ab07ac8c90e5f073a0b72521165da19a to your computer and use it in GitHub Desktop.
Save b5/ab07ac8c90e5f073a0b72521165da19a to your computer and use it in GitHub Desktop.
constructing a private file in WNFS v2
package wnfs
import (
"context"
"testing"
mockipfs "github.com/qri-io/wnfs-go/ipfs/mock"
"github.com/qri-io/wnfs-go/mdstore"
)
func TestPrivateFilePrimitives(t *testing.T) {
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
// allocate a store
ipfs, err := mockipfs.MockMerkleDagStore(ctx)
if err != nil {
t.Fatal(err)
}
// create a spiral ratchet our source of keys
seed := shasumFromHex("600b56e66b7d12e08fd58544d7c811db0063d7aa467a1f6be39990fed0ca5b33")
r := NewSpiralRatchetFromSeed(seed)
r.Advance()
key := r.Key()
// construct & encrypt a file using the ratchet's key
// b/c it's symmetric crypto we're not bound by webcrypto api, we can use
// whatever encryption format we'd like
// what if we did multiformats for symmetric encryption?
// this is because multiformats and the decentralized identity foundation are
// at an impasse.
// where would you put a spec for symmetric keys?
fileContents := []byte("hello world!")
// uhh... why not compress before encrypt?
encrypted, err := encrypt(key, fileContents)
if err != nil {
t.Fatal(err)
}
// write encrypted file to IPFS
res, err := ipfs.PutFile(NewMemfileBytes("", encrypted))
if err != nil {
t.Fatal(err)
}
// construct wnfs file SNode
in := NewINumber() // create an inumber for this file
idBnf := identityBareNamefilter() // this file is the root, use identity bnf as the "parent"
// TODO(b5): BareNameFilter should NOT accept a read key
// in the bare name filter. In v1 the read key was the inumber, in v2,
// the inumber is the inumber, and the readkey is the revision
bnf, err := NewBareNamefilter(idBnf, in) // construct the namefilter
if err != nil {
t.Fatal(err)
}
// there's nothing preventing
snode := &PrivateFile{
// fs: ipfs,
info: PrivateFileInfo{
Ratchet: r.Encode(),
BareNamefilter: bnf,
ContentID: res.Cid,
INumber: in,
// TODO: finish this.
Metadata: nil,
},
}
// writes are done by UCAN, not by ratchet. b/c we're in the browser we
// need revokability. Write access is stored by
// likely storing UCANS on the outside of each node
// in v1 we'd store a barename filter of the things you're allowed to write to
// in v2 it's the inumber of the node you have write access to written to a BNF
// all parents must be in the BNF
// today UCANS are stored in an HTTP header
// in the future a POST request will have a CID for the UCAN in the request env
// { "data": []byte, "prf": "CID_OF_UCAN|UCAN"}
// don't love that it exposes the public keys that are doing all the activity
// considering zk proof
// BUT, b/c a single agent can generate n-keys, we can use the TOR approach,
// which is obfuscation, not secrecy
// to construct a new name you need the inumber, ratchet, and bnf of parent
// encrypt, write to IPFS
encInfo, err := snode.encrypt(key)
if err != nil {
t.Fatal(err)
}
infoRes, err := ipfs.PutFile(NewMemfileBytes("", encInfo))
if err != nil {
t.Fatal(err)
}
// build secret name for SNode
rnf, err := AddKey(bnf, key)
if err != nil {
t.Fatal(err)
}
privateName, err := ToPrivateName(rnf)
if err != nil {
t.Fatal(err)
}
// construct pointer machine
mmpt := NewMMPT(ipfs, mdstore.NewLinks())
err = mmpt.Add(string(privateName), infoRes.Cid)
if err != nil {
t.Fatal(err)
}
id, err := mmpt.Get(string(privateName))
if err != nil {
t.Fatal(err)
}
if !infoRes.Cid.Equals(id) {
t.Errorf("MMPT response mismatch want %q got %q", infoRes.Cid, id)
}
// trees: key needs to be stored on directories, private links prop contains
// read key
// to get into the top of whatever portion you have access to: keep a pointer
// to the last cid that you know about, and the key
// you need to FF each node b/c you're constructing the next revision number
// (thus, it's name), and looking for it in the MMPT
// TODO(b5): construct a private directory
// TODO(b5) read the file back
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment