Created
August 20, 2021 19:07
-
-
Save b5/ab07ac8c90e5f073a0b72521165da19a to your computer and use it in GitHub Desktop.
constructing a private file in WNFS v2
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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