From b69831ae58d35d79a993a3dfda8e987bbdaee75a Mon Sep 17 00:00:00 2001 From: Michael Andersen Date: Tue, 2 Jun 2015 20:10:23 -0700 Subject: [PATCH] Progress is great --- adapter/oob/main.go | 26 ++- api/async_full.go | 38 ++-- api/bosswave.go | 9 +- api/chainbuilder.go | 141 ++++++++++--- api/peerclient.go | 6 +- bw.go | 404 +++++++++++++++++++++++++++----------- internal/core/message.go | 16 +- internal/core/services.go | 3 +- objects/routing.go | 109 ++++++++++ 9 files changed, 576 insertions(+), 176 deletions(-) diff --git a/adapter/oob/main.go b/adapter/oob/main.go index 7e9b02e..c1b9b82 100644 --- a/adapter/oob/main.go +++ b/adapter/oob/main.go @@ -8,6 +8,7 @@ import ( "net" "os" "strconv" + "strings" "sync" "time" @@ -84,17 +85,23 @@ func (a *Adapter) handleClient(conn net.Conn) { } } -func loadCommonURI(f *objects.Frame) ([]byte, string, bool) { +func loadCommonURI(f *objects.Frame, bw *api.BW) ([]byte, string, bool) { mvk, mvkOk := f.GetFirstHeader("mvk") var rmvk []byte uri, uriOk := f.GetFirstHeader("uri") suffix, suffixOk := f.GetFirstHeader("uri_suffix") if uriOk { - var ok bool - rmvk, suffix, ok = api.SplitURI(uri) - if !ok { + var err error + parts := strings.SplitN(uri, "/", 2) + if len(parts) != 2 { + return nil, "", false + } + rmvk, err = bw.ResolveName(parts[0]) + if err != nil { + log.Info("Could not resolve uri: " + parts[0] + ":" + err.Error()) return nil, "", false } + suffix = parts[1] } else if !(mvkOk && suffixOk) { return nil, "", false } else { @@ -221,7 +228,7 @@ func dispatchFrame(bwcl *api.BosswaveClient, f *objects.Frame, send func(f *obje } switch f.Cmd { case objects.CmdPublish, objects.CmdPersist: - mvk, suffix, ok := loadCommonURI(f) + mvk, suffix, ok := loadCommonURI(f, bwcl.BW()) if !ok { err("malformed URI components") return @@ -267,7 +274,7 @@ func dispatchFrame(bwcl *api.BosswaveClient, f *objects.Frame, send func(f *obje bwcl.Publish(p, mkGenericActionCB(replyto, send)) return case objects.CmdList: - mvk, suffix, ok := loadCommonURI(f) + mvk, suffix, ok := loadCommonURI(f, bwcl.BW()) if !ok { err("malformed URI components") return @@ -318,7 +325,7 @@ func dispatchFrame(bwcl *api.BosswaveClient, f *objects.Frame, send func(f *obje } unpack = cx } - mvk, suffix, ok := loadCommonURI(f) + mvk, suffix, ok := loadCommonURI(f, bwcl.BW()) if !ok { err("malformed URI components") return @@ -377,7 +384,7 @@ func dispatchFrame(bwcl *api.BosswaveClient, f *objects.Frame, send func(f *obje } unpack = cx } - mvk, suffix, ok := loadCommonURI(f) + mvk, suffix, ok := loadCommonURI(f, bwcl.BW()) if !ok { err("malformed URI components") return @@ -409,6 +416,7 @@ func dispatchFrame(bwcl *api.BosswaveClient, f *objects.Frame, send func(f *obje } bwcl.Subscribe(p, func(status int, isNew bool, id core.UniqueMessageID, msg string) { + log.Infof("Got action CB for sub: %v %v %v", status, isNew, msg) if status == core.BWStatusOkay { r := objects.CreateFrame(objects.CmdResponse, replyto) r.AddHeader("status", "okay") @@ -556,7 +564,7 @@ func dispatchFrame(bwcl *api.BosswaveClient, f *objects.Frame, send func(f *obje } if !ispermission { - mvk, suffix, ok := loadCommonURI(f) + mvk, suffix, ok := loadCommonURI(f, bwcl.BW()) if !ok { err("access DOTs require URI") return diff --git a/api/async_full.go b/api/async_full.go index c9f0b0a..e93fa78 100644 --- a/api/async_full.go +++ b/api/async_full.go @@ -62,10 +62,21 @@ type PublishCallback func(status int, msg string) func (c *BosswaveClient) checkAddOriginVK(m *core.Message) { - if m.PrimaryAccessChain == nil || - m.PrimaryAccessChain.GetReceiverVK() == nil || - objects.IsEveryoneVK(m.PrimaryAccessChain.GetReceiverVK()) { - fmt.Println("Adding an origin VK header") + //Although the PAC may not be elaborated, we might be able to + //elaborate it some more here for our decision support + pac := m.PrimaryAccessChain + if pac != nil { + if !pac.IsElaborated() { + dc := core.ElaborateDChain(m.PrimaryAccessChain) + if dc != nil { + pac = dc + } + } + core.ResolveDotsInDChain(pac, nil) + } + if pac == nil || !pac.IsElaborated() || + pac.GetReceiverVK() == nil || + objects.IsEveryoneVK(pac.GetReceiverVK()) { ovk := objects.CreateOriginVK(c.us.GetVK()) m.RoutingObjects = append(m.RoutingObjects, ovk) vk := c.us.GetVK() @@ -474,15 +485,18 @@ func (c *BosswaveClient) CreateEntity(p *CreateEntityParams) *objects.Entity { func (c *BosswaveClient) doPAC(m *core.Message, elaboratePAC int) (int, string) { //If there is no explicit PAC, use the first access chain in the ROs - if m.PrimaryAccessChain == nil { - for _, ro := range m.RoutingObjects { - if ro.GetRONum() == objects.ROAccessDChain || - ro.GetRONum() == objects.ROAccessDChainHash { - m.PrimaryAccessChain = ro.(*objects.DChain) - break + //NOPE because sometimes you want to send access chains but not treat + //it as the PAC + /* + if m.PrimaryAccessChain == nil { + for _, ro := range m.RoutingObjects { + if ro.GetRONum() == objects.ROAccessDChain || + ro.GetRONum() == objects.ROAccessDChainHash { + m.PrimaryAccessChain = ro.(*objects.DChain) + break + } } - } - } + }*/ //Elaborate PAC if elaboratePAC > NoElaboration { if m.PrimaryAccessChain == nil { diff --git a/api/bosswave.go b/api/bosswave.go index bb83099..d8a5c69 100644 --- a/api/bosswave.go +++ b/api/bosswave.go @@ -7,6 +7,7 @@ import ( "math/rand" "net" "os" + "strconv" "strings" "sync" @@ -70,7 +71,7 @@ func OpenBWContext(config *core.BWConfig) *BW { os.Exit(1) } rv.Entity = objects.CreateLightEntity(rVK, rSK) - rv.MVKs = make([][]byte, len(config.Affinity.MVK)) + rv.MVKs = make([][]byte, len(config.Affinity.MVK)+1) for i, smvk := range config.Affinity.MVK { mvk, err := crypto.UnFmtKey(smvk) if err != nil { @@ -79,6 +80,7 @@ func OpenBWContext(config *core.BWConfig) *BW { } rv.MVKs[i] = mvk } + rv.MVKs[len(config.Affinity.MVK)] = rv.Entity.GetVK() rocks.Initialize(config.Router.DB) return rv } @@ -187,8 +189,9 @@ func (bw *BW) GetTarget(drvk string) (string, error) { if len(addrs) < 1 { return "", errors.New("Unable to resolve VK to router") } - bw.Targetcache[drvk] = addrs[0].Target - return addrs[0].Target, nil + tgt := addrs[0].Target[:len(addrs[0].Target)-1] + ":" + strconv.Itoa(int(addrs[0].Port)) + bw.Targetcache[drvk] = tgt + return tgt, nil } func (bw *BW) GetDRVK(mvk string) ([]byte, error) { bw.cachelock.Lock() diff --git a/api/chainbuilder.go b/api/chainbuilder.go index 67735c2..cd946e1 100644 --- a/api/chainbuilder.go +++ b/api/chainbuilder.go @@ -1,8 +1,10 @@ package api import ( + "bytes" "container/list" "errors" + "fmt" "strings" "sync" @@ -17,44 +19,70 @@ type ChainBuilder struct { status chan string uri string perms string + target []byte peers [][]byte } type scenario struct { - chain []*objects.DOT + chain []*objects.DOT + suffix string } +func (s *scenario) TTL() int { + ttl := 256 + for _, d := range s.chain { + ttl = ttl - 1 + if d.GetTTL() < ttl { + ttl = d.GetTTL() + } + } + return ttl +} func (s *scenario) Clone() *scenario { cc := make([]*objects.DOT, len(s.chain)) copy(cc, s.chain) return &scenario{chain: cc} } - -func (s *scenario) AddAndClone(d *objects.DOT) *scenario { +func (s *scenario) String() string { + rv := "[" + for _, d := range s.chain { + rv += crypto.FmtKey(d.GetHash()) + "," + } + return rv + "]" +} +func NewScenario(d *objects.DOT) *scenario { + return &scenario{chain: []*objects.DOT{d}, suffix: d.GetAccessURISuffix()} +} +func (s *scenario) AddAndClone(d *objects.DOT) (*scenario, bool) { cc := make([]*objects.DOT, len(s.chain)+1) copy(cc, s.chain) cc[len(s.chain)] = d - return &scenario{chain: cc} + nuri, okay := util.RestrictBy(s.suffix, d.GetAccessURISuffix()) + if !okay { + return nil, false + } + return &scenario{chain: cc, suffix: nuri}, true } -/* -func (s *scenario) GetTerminalVK() string { - return crypto.FmtKey(s.chain[len(s.chain)-1].GetReceiverVK()) -}*/ +func (s *scenario) GetTerminalVK() []byte { + return s.chain[len(s.chain)-1].GetReceiverVK() +} -func NewChainBuilder(cl *BosswaveClient, uri, perms string, status chan string) *ChainBuilder { - return &ChainBuilder{cl: cl, uri: uri, perms: perms, peers: make([][]byte, 0), status: status} +func (s *scenario) ToChain() *objects.DChain { + rv, err := objects.CreateDChain(true, s.chain...) + if err != nil { + panic(err) + } + return rv +} +func NewChainBuilder(cl *BosswaveClient, uri, perms string, target []byte, status chan string) *ChainBuilder { + return &ChainBuilder{cl: cl, uri: uri, target: target, perms: perms, peers: make([][]byte, 0), status: status} } func (b *ChainBuilder) AddPeerMVK(mvk []byte) { b.peers = append(b.peers, mvk) } -//genTier takes an entity and finds every DOT that grants on -//the MVK with the required permissions -func (b *ChainBuilder) genTier(used map[string]bool, from []byte) { - -} func (b *ChainBuilder) dotUseful(d *objects.DOT) bool { return true } @@ -64,10 +92,16 @@ func (b *ChainBuilder) getOptions(from []byte) []*objects.DOT { wg := sync.WaitGroup{} go func() { for _, peerMVK := range b.peers { + drVK, err := b.cl.BW().GetDRVK(crypto.FmtKey(peerMVK)) + if err != nil { + b.status <- "could not get DRVK for peer " + crypto.FmtKey(peerMVK) + continue + } wg.Add(1) + //The peer might be an MVK, but its the DR itself that we need to query go b.cl.Query(&QueryParams{ - MVK: peerMVK, - URISuffix: "$/fromto/" + crypto.FmtKey(from)[:43] + "/+", + MVK: drVK, + URISuffix: "$/dot/fromto/" + crypto.FmtKey(from)[:43] + "/+", }, func(status int, msg string) { if status != core.BWStatusOkay { @@ -77,13 +111,18 @@ func (b *ChainBuilder) getOptions(from []byte) []*objects.DOT { }, func(m *core.Message) { if m == nil { + b.status <- "finished options query" wg.Done() return } + b.status <- "got options query rv" for _, ro := range m.RoutingObjects { dot, ok := ro.(*objects.DOT) - if ok && b.dotUseful(dot) { - rc <- dot + if ok { + core.DistributeRO(b.cl.BW().Entity, dot, b.cl.CL()) + if b.dotUseful(dot) { + rc <- dot + } } } }) @@ -91,37 +130,79 @@ func (b *ChainBuilder) getOptions(from []byte) []*objects.DOT { wg.Wait() close(rc) }() + seen := make(map[string]bool) for res := range rc { - rv = append(rv, res) + k := crypto.FmtHash(res.GetHash()) + _, ok := seen[k] + if !ok { + rv = append(rv, res) + seen[k] = true + } } + b.status <- fmt.Sprintf("options len %d", len(rv)) return rv } -func (b *ChainBuilder) Build() (*objects.DChain, error) { +func (b *ChainBuilder) Build() ([]*objects.DChain, error) { parts := strings.SplitN(b.uri, "/", 2) if len(parts) != 2 { return nil, errors.New("Invalid URI") } - valid, hasStar, hasPlus, hasDollar, hasBang := util.AnalyzeSuffix(parts[1]) + valid, _, _, _, _ := util.AnalyzeSuffix(parts[1]) if !valid { return nil, errors.New("Invalid URI") } - mvk, err := crypto.UnFmtKey(parts[0]) + mvk, err := b.cl.BW().ResolveName(parts[0]) if err != nil { return nil, err } - //The VK's we have visited (and the TTL). Do not add scenarios that have seen this VK - //unless the TTL is higher because it's a - seen := map[string]bool validscenarios := list.New() evals := list.New() + b.status <- "populating initial options" + b.status <- "looking for DOTs from " + crypto.FmtKey(mvk) initial := b.getOptions(mvk) for _, dt := range initial { - s := scenario{chain: []*objects.DOT{dt}} - evals.PushBack(&s) + s := NewScenario(dt) + if bytes.Equal(s.GetTerminalVK(), b.target) { + b.status <- "found valid scenario" + validscenarios.PushBack(s) + } else { + b.status <- "adding scenario: " + s.String() + evals.PushBack(s) + } } for evals.Front() != nil { - s := evals.Front() - evals.Remove(s) + le := evals.Front() + s := le.Value.(*scenario) + endsat := s.GetTerminalVK() + opts := b.getOptions(endsat) + for _, dt := range opts { + newscenario, okay := s.AddAndClone(dt) + if !okay { + continue + } + if bytes.Equal(newscenario.GetTerminalVK(), b.target) { + b.status <- "found valid scenario" + validscenarios.PushBack(newscenario) + } else { + evals.PushBack(newscenario) + } + } + evals.Remove(le) + } + fmt.Println("uh") + seen := make(map[string]bool) + rv := make([]*objects.DChain, 0, validscenarios.Len()) + e := validscenarios.Front() + for e != nil { + chn := e.Value.(*scenario).ToChain() + k := crypto.FmtHash(chn.GetChainHash()) + _, ok := seen[k] + if !ok { + rv = append(rv, chn) + } + e = e.Next() } + close(b.status) + return rv, nil } diff --git a/api/peerclient.go b/api/peerclient.go index 15a2345..fc1439a 100644 --- a/api/peerclient.go +++ b/api/peerclient.go @@ -137,8 +137,10 @@ func (pc *PeerClient) Subscribe(m *core.Message, seqno: pc.getSeqno(), } pc.transact(&nf, func(f *nativeFrame) { + log.Infof("got sub response cmd: %d", f.cmd) switch f.cmd { - case nCmdRStatus: + case nCmdRSub: + log.Infof("Got subscribe status response") if len(f.body) < 2 { actionCB(core.BWStatusPeerError, false, core.UniqueMessageID{}, "short response frame") return @@ -162,7 +164,7 @@ func (pc *PeerClient) Subscribe(m *core.Message, } s := nm.Verify() if s.Code != core.BWStatusOkay { - log.Infof("dropping incoming subscription result on uri=%s (failed local validation)", m.Topic) + log.Infof("dropping incoming subscription result on uri=%s (failed local validation)", nm.Topic) return } messageCB(nm) diff --git a/bw.go b/bw.go index 3e517e5..69af732 100644 --- a/bw.go +++ b/bw.go @@ -4,7 +4,10 @@ import ( "fmt" "io/ioutil" "os" + "path" + "strconv" "strings" + "sync" "time" "github.com/codegangsta/cli" @@ -16,6 +19,7 @@ import ( "github.com/immesys/bw2/internal/util" "github.com/immesys/bw2/objects" "github.com/mgutz/ansi" + homedir "github.com/mitchellh/go-homedir" ) func main() { @@ -175,6 +179,38 @@ func main() { pflag, }, }, + { + Name: "buildchain", + Aliases: []string{"bc"}, + Usage: "build a DOT Chain", + Action: actionBuildChain, + Flags: []cli.Flag{ + cli.StringSliceFlag{ + Name: "router, r", + Usage: "a router to query for DOTs (given as an MVK / DNS alias)", + Value: &cli.StringSlice{}, + }, + cli.StringFlag{ + Name: "uri, u", + Usage: "the URI to build a chain for", + Value: "", + }, + cli.StringFlag{ + Name: "permissions, x", + Usage: "the permissions to try build", + Value: "PCL", + }, + cli.StringFlag{ + Name: "to, t", + Usage: "the VK to build a chain to", + Value: "", + }, + cli.BoolFlag{ + Name: "verbose, v", + Usage: "dump the verbose details of all chains", + }, + }, + }, /* { Name: "import", @@ -240,12 +276,12 @@ func getTempBW(c *cli.Context) *api.BW { if len(scfg) != 0 { cfg = core.LoadConfig(scfg) } else { - dbpath, err := ioutil.TempDir("", "bw2cli") + hm, err := homedir.Dir() if err != nil { - fmt.Println("ERROR: could not create CLI temporary database: ", err.Error()) - os.Exit(1) + panic(err) } - cfg := &core.BWConfig{} + dbpath := path.Join(hm, ".bw.cli.db") + cfg = &core.BWConfig{} cfg.Router.DB = dbpath //router keys are irrelevant, save generation entropy by hardcoding cfg.Router.SK = "NmSf-XwX0rtbaIVUhnxMoL_pBkRdUWyfX6nf4zpR8Rk=" @@ -320,7 +356,7 @@ func actionMkEntity(c *cli.Context) { fname := c.String("outfile") if len(fname) == 0 { - fname = "bw2." + crypto.FmtKey(ent.GetVK()) + ".key" + fname = "." + crypto.FmtKey(ent.GetVK()) + ".key" } wrapped := make([]byte, len(ent.GetSigningBlob())+1) copy(wrapped[1:], ent.GetSigningBlob()) @@ -378,9 +414,8 @@ func actionMkDOT(c *cli.Context) { if err != nil { possibleMVK, err := bw.ResolveName(parts[0]) if err == nil { - fmt.Println("You cannot use a DNS alias for the MVK in a DOT URI for security reasons") - fmt.Printf("check this URI is correct and then specify it explicitly:\n%s\n", crypto.FmtKey(possibleMVK)+"/"+parts[1]) - doExit(bw, 1, "") + fmt.Printf("NOTE, resolved DNS aliased URI to :%s\n", crypto.FmtKey(possibleMVK)+"/"+parts[1]) + mvk = possibleMVK } else { fmt.Println("could not parse the MVK in the URI") } @@ -413,12 +448,12 @@ func actionMkDOT(c *cli.Context) { fname := c.String("outfile") if len(fname) == 0 { - fname = "bw2." + crypto.FmtKey(dot.GetHash()) + ".dot" + fname = "." + crypto.FmtKey(dot.GetHash()) + ".dot" } wrapped := make([]byte, len(dot.GetContent())+1) copy(wrapped[1:], dot.GetContent()) wrapped[0] = objects.ROAccessDOT - err = ioutil.WriteFile(fname, wrapped, 0600) + err = ioutil.WriteFile(fname, wrapped, 0666) if err != nil { doExit(bw, 1, "could not write dot to: "+fname) } @@ -453,109 +488,170 @@ func ifstring(level int) string { for i := 0; i < level-2; i++ { rv += codes[i] + "\u2503" } - return rv + codes[level-2] + "\u2523" + codes[level-1] + "\u2533" -} -func actionInspect(c *cli.Context) { - bw := getTempBW(c) - cl := bw.CreateClient() + if level >= 2 { + return rv + codes[level-2] + "\u2523" + codes[level-1] + "\u2533" + } else { + return codes[level-1] + "\u2533" + } - doentity := func(e *objects.Entity, indent int) { - fmt.Println(ifstring(indent) + " Entity " + crypto.FmtKey(e.GetVK())) - if e.SigValid() { - fmt.Println(istring(indent) + " Signature valid") - } else { - fmt.Println(istring(indent) + " Signature INVALID") - } - if len(e.GetSK()) != 0 { - fmt.Println(istring(indent)+" SK: ", crypto.FmtKey(e.GetSK())) - keysOk := crypto.CheckKeypair(e.GetSK(), e.GetVK()) - if keysOk { - fmt.Println(istring(indent) + " Keypair: ok") - } else { - fmt.Println(istring(indent) + " Keypair: INCONSISTENT") - } - } - if len(e.GetContact()) != 0 { - fmt.Println(istring(indent) + " Contact: " + e.GetContact()) - } - if len(e.GetComment()) != 0 { - fmt.Println(istring(indent) + " Comment: " + e.GetComment()) - } - if e.GetCreated() != nil { - fmt.Println(istring(indent) + " Created: " + e.GetCreated().Format(time.RFC3339)) - } - if e.GetExpiry() != nil { - fmt.Println(istring(indent) + " Expires: " + e.GetExpiry().Format(time.RFC3339)) - } - for _, rvk := range e.GetRevokers() { - fmt.Println(istring(indent) + " Revoker:" + crypto.FmtKey(rvk)) - } +} +func doentity(e *objects.Entity, indent int) { + fmt.Println(ifstring(indent) + " Entity " + crypto.FmtKey(e.GetVK())) + if e.SigValid() { + fmt.Println(istring(indent) + " Signature valid") + } else { + fmt.Println(istring(indent) + " Signature INVALID") } - dodot := func(d *objects.DOT, indent int) { - fmt.Println(ifstring(indent) + " DOT " + crypto.FmtHash(d.GetHash())) - if d.SigValid() { - fmt.Println(istring(indent) + " Signature valid") - } else { - fmt.Println(istring(indent) + " Signature INVALID") - } - fmt.Println(istring(indent) + " From: " + crypto.FmtKey(d.GetGiverVK())) - fe, ok := store.GetEntity(d.GetGiverVK()) - if ok { - doentity(fe, indent+1) + if len(e.GetSK()) != 0 { + fmt.Println(istring(indent)+" SK: ", crypto.FmtKey(e.GetSK())) + keysOk := crypto.CheckKeypair(e.GetSK(), e.GetVK()) + if keysOk { + fmt.Println(istring(indent) + " Keypair: ok") } else { - fmt.Println(ifstring(indent+1) + " Unknown Entity") - } - fmt.Println(istring(indent) + " To: " + crypto.FmtKey(d.GetGiverVK())) - fe, ok = store.GetEntity(d.GetReceiverVK()) - if ok { - doentity(fe, indent+1) - } else { - fmt.Println(ifstring(indent+1) + " Unknown Entity") - } - if d.IsAccess() { - fmt.Println(istring(indent) + " URI: " + crypto.FmtKey(d.GetAccessURIMVK()) + "/" + d.GetAccessURISuffix()) - fmt.Println(istring(indent) + " Permissions: " + d.GetPermString()) - } - if len(d.GetContact()) != 0 { - fmt.Println(istring(indent) + " Contact: " + d.GetContact()) - } - if len(d.GetComment()) != 0 { - fmt.Println(istring(indent) + " Comment: " + d.GetComment()) - } - if d.GetCreated() != nil { - fmt.Println(istring(indent) + " Created: " + d.GetCreated().Format(time.RFC3339)) - } - if d.GetExpiry() != nil { - fmt.Println(istring(indent) + " Expires: " + d.GetExpiry().Format(time.RFC3339)) - } - for _, rvk := range d.GetRevokers() { - fmt.Println(istring(indent) + " Revoker:" + crypto.FmtKey(rvk)) + fmt.Println(istring(indent) + " Keypair: INCONSISTENT") } } - dochain := func(contents []byte, indent int) { - ro, err := objects.LoadRoutingObject(int(contents[0]), contents[1:]) - if err != nil { - fmt.Println("ERR: could not parse") - return - } - dc := ro.(*objects.DChain) - fmt.Println(ifstring(indent)+" DChain ", crypto.FmtHash(dc.GetChainHash())) - if !dc.IsElaborated() { - fmt.Println(istring(indent) + " Elaborated: False") - } else { - fmt.Println(istring(indent) + " Elaborated: True") - for i := 0; i < dc.NumHashes(); i++ { - dh := dc.GetDotHash(i) - fmt.Printf(istring(indent)+" DOT[%d] = %s\n", i, crypto.FmtHash(dh)) - dt, ok := store.GetDOT(dh) - if ok { + if len(e.GetContact()) != 0 { + fmt.Println(istring(indent) + " Contact: " + e.GetContact()) + } + if len(e.GetComment()) != 0 { + fmt.Println(istring(indent) + " Comment: " + e.GetComment()) + } + if e.GetCreated() != nil { + fmt.Println(istring(indent) + " Created: " + e.GetCreated().Format(time.RFC3339)) + } + if e.GetExpiry() != nil { + fmt.Println(istring(indent) + " Expires: " + e.GetExpiry().Format(time.RFC3339)) + } + for _, rvk := range e.GetRevokers() { + fmt.Println(istring(indent) + " Revoker:" + crypto.FmtKey(rvk)) + } +} +func dodot(d *objects.DOT, indent int) { + fmt.Println(ifstring(indent) + " DOT " + crypto.FmtHash(d.GetHash())) + if d.SigValid() { + fmt.Println(istring(indent) + " Signature valid") + } else { + fmt.Println(istring(indent) + " Signature INVALID") + } + fmt.Println(istring(indent) + " From: " + crypto.FmtKey(d.GetGiverVK())) + fe, ok := store.GetEntity(d.GetGiverVK()) + if ok { + doentity(fe, indent+1) + } else { + fmt.Println(ifstring(indent+1) + " Unknown Entity") + } + fmt.Println(istring(indent) + " To: " + crypto.FmtKey(d.GetReceiverVK())) + fe, ok = store.GetEntity(d.GetReceiverVK()) + if ok { + doentity(fe, indent+1) + } else { + fmt.Println(ifstring(indent+1) + " Unknown Entity") + } + if d.IsAccess() { + fmt.Println(istring(indent) + " URI: " + crypto.FmtKey(d.GetAccessURIMVK()) + "/" + d.GetAccessURISuffix()) + fmt.Println(istring(indent) + " Permissions: " + d.GetPermString()) + } + if len(d.GetContact()) != 0 { + fmt.Println(istring(indent) + " Contact: " + d.GetContact()) + } + if len(d.GetComment()) != 0 { + fmt.Println(istring(indent) + " Comment: " + d.GetComment()) + } + if d.GetCreated() != nil { + fmt.Println(istring(indent) + " Created: " + d.GetCreated().Format(time.RFC3339)) + } + if d.GetExpiry() != nil { + fmt.Println(istring(indent) + " Expires: " + d.GetExpiry().Format(time.RFC3339)) + } + for _, rvk := range d.GetRevokers() { + fmt.Println(istring(indent) + " Revoker:" + crypto.FmtKey(rvk)) + } +} +func dochain(dc *objects.DChain, indent int, verbose bool) { + fmt.Println(ifstring(indent)+" DChain ", crypto.FmtHash(dc.GetChainHash())) + if !dc.IsElaborated() { + fmt.Println(istring(indent) + " Elaborated: False") + } else { + fmt.Println(istring(indent) + " Elaborated: True") + haveall := true + for i := 0; i < dc.NumHashes(); i++ { + dh := dc.GetDotHash(i) + fmt.Printf(istring(indent)+" DOT[%d] = %s\n", i, crypto.FmtHash(dh)) + var dt *objects.DOT + dt = dc.GetDOT(i) + if dt == nil { + dt, _ = store.GetDOT(dh) + } + if dt != nil { + dc.SetDOT(i, dt) + if verbose { dodot(dt, indent+1) - } else { + } + + } else { + haveall = false + if verbose { fmt.Println(ifstring(indent+1) + " DOT is not resolvable") } } } + if haveall { + fmt.Println(istring(indent) + " Grants: " + dc.GetAccessURIPermString()) + suffix, err := dc.GetAccessURISuffix() + if err != nil { + fmt.Println(istring(indent) + " On: ") + } else { + fmt.Println(istring(indent) + " On: " + crypto.FmtKey(dc.GetMVK()) + "/" + suffix) + } + fmt.Println(istring(indent) + " End TTL: " + strconv.Itoa(dc.GetTTL())) + } else { + fmt.Println(istring(indent) + " TTL/Grant/URI unknown (missing DOTs)") + } + } +} +func distEntity(e *objects.Entity, cl *api.BosswaveClient, to []string) { + core.DistributeRO(cl.BW().Entity, e, cl.CL()) + for _, tgt := range to { + publishROs(cl, tgt, []objects.RoutingObject{e}) + } +} +func distDOT(d *objects.DOT, cl *api.BosswaveClient, to []string) { + core.DistributeRO(cl.BW().Entity, d, cl.CL()) + for _, tgt := range to { + publishROs(cl, tgt, []objects.RoutingObject{d}) + } + fe, ok := store.GetEntity(d.GetGiverVK()) + if ok { + distEntity(fe, cl, to) + } + fe, ok = store.GetEntity(d.GetReceiverVK()) + if ok { + distEntity(fe, cl, to) + } +} +func distChain(c *objects.DChain, cl *api.BosswaveClient, to []string) { + if !c.IsElaborated() { + fmt.Printf("Chain is not elaborated?") + return + } + core.DistributeRO(cl.BW().Entity, c, cl.CL()) + for _, tgt := range to { + publishROs(cl, tgt, []objects.RoutingObject{c}) + } + for i := 0; i < c.NumHashes(); i++ { + dt, ok := store.GetDOT(c.GetDotHash(i)) + if ok { + distDOT(dt, cl, to) + } } +} +func actionInspect(c *cli.Context) { + bw := getTempBW(c) + cl := bw.CreateClient() + rent := getRandomEntity(cl) + cl.SetEntityObj(rent) + for _, fl := range c.Args() { fmt.Println("Inspecting: ", fl, ansi.ColorCode("reset")) contents, err := ioutil.ReadFile(fl) @@ -570,8 +666,8 @@ func actionInspect(c *cli.Context) { fmt.Println("ERR: could not parse: " + err.Error()) return } - core.DistributeRO(bw.Entity, ro, cl.CL()) - doentity(ro.(*objects.Entity), 1) + distEntity(ro.(*objects.Entity), cl, c.StringSlice("publishto")) + doentity(ro.(*objects.Entity), 2) case objects.ROEntityWKey: fmt.Println("\u2533 Type: Entity key file") ro, err := objects.LoadRoutingObject(objects.ROEntity, contents[33:]) @@ -579,10 +675,10 @@ func actionInspect(c *cli.Context) { fmt.Println("ERR: could not parse: " + err.Error()) return } - core.DistributeRO(bw.Entity, ro, cl.CL()) ent := ro.(*objects.Entity) + distEntity(ent, cl, c.StringSlice("publishto")) ent.SetSK(contents[1:33]) - doentity(ro.(*objects.Entity), 1) + doentity(ro.(*objects.Entity), 2) case objects.ROAccessDOT: fmt.Println("\u2533 Type: Access DOT") ro, err := objects.LoadRoutingObject(int(contents[0]), contents[1:]) @@ -590,8 +686,8 @@ func actionInspect(c *cli.Context) { fmt.Println("ERR: could not parse: " + err.Error()) return } - core.DistributeRO(bw.Entity, ro, cl.CL()) dot := ro.(*objects.DOT) + distDOT(dot, cl, c.StringSlice("publishto")) dodot(dot, 2) case objects.ROPermissionDOT: fmt.Println("\u2533 Type: Application permission DOT") @@ -600,27 +696,109 @@ func actionInspect(c *cli.Context) { fmt.Println("ERR: could not parse: " + err.Error()) return } - core.DistributeRO(bw.Entity, ro, cl.CL()) dot := ro.(*objects.DOT) - dodot(dot, 1) + distDOT(dot, cl, c.StringSlice("publishto")) + dodot(dot, 2) case objects.ROPermissionDChain: fmt.Println("\u2533 Type: Permission DCHain") - dochain(contents, 1) + ro, err := objects.LoadRoutingObject(int(contents[0]), contents[1:]) + if err != nil { + fmt.Println("ERR: could not parse") + return + } + dc := ro.(*objects.DChain) + distChain(dc, cl, c.StringSlice("publishto")) + dochain(dc, 2, true) case objects.ROPermissionDChainHash: fmt.Println("\u2533 Type: Permission DChain hash") - dochain(contents, 1) + ro, err := objects.LoadRoutingObject(int(contents[0]), contents[1:]) + if err != nil { + fmt.Println("ERR: could not parse") + return + } + dc := ro.(*objects.DChain) + distChain(dc, cl, c.StringSlice("publishto")) + dochain(dc, 2, true) case objects.ROAccessDChain: fmt.Println("\u250f Type: Access DChain") - dochain(contents, 1) + ro, err := objects.LoadRoutingObject(int(contents[0]), contents[1:]) + if err != nil { + fmt.Println("ERR: could not parse") + return + } + dc := ro.(*objects.DChain) + distChain(dc, cl, c.StringSlice("publishto")) + dochain(dc, 2, true) case objects.ROAccessDChainHash: fmt.Println("\u2533 Type: Access DChain hash") - dochain(contents, 1) + ro, err := objects.LoadRoutingObject(int(contents[0]), contents[1:]) + if err != nil { + fmt.Println("ERR: could not parse") + return + } + dc := ro.(*objects.DChain) + distChain(dc, cl, c.StringSlice("publishto")) + dochain(dc, 2, true) default: fmt.Println("ERR: not a Routing Object file") } } fmt.Print(ansi.ColorCode("reset")) } +func actionBuildChain(c *cli.Context) { + bw := getTempBW(c) + cl := bw.CreateClient() + rent := getRandomEntity(cl) + cl.SetEntityObj(rent) + uri := c.String("uri") + perms := c.String("permissions") + target, err := crypto.UnFmtKey(c.String("to")) + if err != nil { + doExit(bw, 1, "Invalid 'to' key") + } + status := make(chan string, 10) + outwg := sync.WaitGroup{} + outwg.Add(1) + go func() { + for s := range status { + fmt.Println("CB: ", s) + } + outwg.Done() + }() + cb := api.NewChainBuilder(cl, uri, perms, target, status) + //Add the CLI router as a peer so we use its database + cb.AddPeerMVK(bw.Entity.GetVK()) + for _, sp := range c.StringSlice("router") { + mvk, err := bw.ResolveName(sp) + if err != nil { + doExit(bw, 1, "Could not resolve router: "+err.Error()) + } + cb.AddPeerMVK(mvk) + } + chains, err := cb.Build() + if err != nil { + doExit(bw, 1, "Builder error: "+err.Error()) + } + fmt.Println("Complete") + for _, dc := range chains { + fmt.Print(ansi.ColorCode("reset")) + fmt.Println("found chain: ", crypto.FmtHash(dc.GetChainHash())) + dochain(dc, 2, c.Bool("verbose")) + fmt.Print(ansi.ColorCode("reset")) + } + for _, dc := range chains { + wrapped := make([]byte, len(dc.GetContent())+1) + copy(wrapped[1:], dc.GetContent()) + wrapped[0] = byte(dc.GetRONum()) + fname := fmt.Sprintf(".%s.chain", crypto.FmtHash(dc.GetChainHash())) + err = ioutil.WriteFile(fname, wrapped, 0666) + if err != nil { + doExit(bw, 1, "could not write dchain to: "+fname) + } + fmt.Println("Wrote chain to file: ", fname) + } + outwg.Wait() +} /* func actionMkEntity(c *cli.Context) { diff --git a/internal/core/message.go b/internal/core/message.go index d6a90fb..d7386f9 100644 --- a/internal/core/message.go +++ b/internal/core/message.go @@ -245,7 +245,7 @@ func ElaborateDChain(dc *objects.DChain) *objects.DChain { func ResolveDotsInDChain(dc *objects.DChain, cache []objects.RoutingObject) bool { if !dc.IsElaborated() { - panic("Can only augment elaborated chain") + return false } //Augment the primary dchain by the ro's we got given for _, ro := range cache { @@ -416,8 +416,6 @@ func (m *Message) Verify() *StatusMessage { fromMVK = true m.status.Code = BWStatusOkay goto endperm - } else { - log.Info("Failed origin MVK check", m.OriginVK, m.MVK) } //These will be populated by the permissions search process @@ -491,10 +489,16 @@ endperm: return &m.status } + if m.OriginVK == nil { + log.Criticalf("V: no origin VK on message") + m.status.Code = BWStatusNoOrigin + return &m.status + } + //Now check if the signature is correct - fmt.Printf("\nenclen %v, sce %v, siglen %v\n", len(m.Encoded), m.SigCoverEnd, len(m.Signature)) - fmt.Println("Signature: ", crypto.FmtSig(m.Signature)) - fmt.Println("VK: ", crypto.FmtKey(*m.OriginVK)) + //fmt.Printf("\nenclen %v, sce %v, siglen %v\n", len(m.Encoded), m.SigCoverEnd, len(m.Signature)) + //fmt.Println("Signature: ", crypto.FmtSig(m.Signature)) + //fmt.Println("VK: ", crypto.FmtKey(*m.OriginVK)) if !crypto.VerifyBlob(*m.OriginVK, m.Signature, m.Encoded[:m.SigCoverEnd]) { m.status.Code = BWStatusInvalidSig log.Infof("V: InvalidSig (whole sig)") diff --git a/internal/core/services.go b/internal/core/services.go index 5649287..0154502 100644 --- a/internal/core/services.go +++ b/internal/core/services.go @@ -9,10 +9,11 @@ import ( ) func makeROMessage(e *objects.Entity, ro objects.RoutingObject, uriSuffix string) *Message { + ovk := objects.CreateOriginVK(e.GetVK()) m := Message{ TopicSuffix: uriSuffix, MVK: e.GetVK(), - RoutingObjects: []objects.RoutingObject{ro}, + RoutingObjects: []objects.RoutingObject{ro, ovk}, PayloadObjects: []objects.PayloadObject{}, } m.Encode(e.GetSK(), e.GetVK()) diff --git a/objects/routing.go b/objects/routing.go index b226753..389e9ff 100644 --- a/objects/routing.go +++ b/objects/routing.go @@ -5,6 +5,7 @@ import ( "crypto/sha256" "encoding/base64" "encoding/binary" + "errors" "fmt" "io" "runtime/debug" @@ -150,6 +151,25 @@ func (ro *DChain) WriteToStream(s io.Writer, fullObjNum bool) error { return nil } +func (ro *DChain) GetAccessURISuffix() (string, error) { + d := ro.dots[0] + u := d.GetAccessURISuffix() + for _, d := range ro.dots[1:] { + nu, ok := util.RestrictBy(u, d.GetAccessURISuffix()) + if !ok { + return "", errors.New("Chain doesn't grant anything") + } + u = nu + } + return u, nil +} +func (ro *DChain) GetAccessURIPermString() string { + adps := ro.dots[0].GetPermissionSet() + for _, d := range ro.dots[1:] { + adps.ReduceBy(d.GetPermissionSet()) + } + return adps.GetPermString() +} func (ro *DChain) IsAccess() bool { return ro.GetRONum() == ROAccessDChain || ro.GetRONum() == ROAccessDChainHash @@ -173,6 +193,21 @@ func (ro *DChain) AugmentBy(d *DOT) { } } +func (ro *DChain) GetTTL() int { + ttl := 256 + for _, d := range ro.dots { + ttl -= 1 + if d.GetTTL() < ttl { + ttl = d.GetTTL() + } + } + return ttl +} + +func (ro *DChain) GetMVK() []byte { + return ro.dots[0].GetAccessURIMVK() +} + //SetDOT sets the specific DOT func (ro *DChain) SetDOT(num int, d *DOT) { ro.dots[num] = d @@ -361,6 +396,80 @@ func (ps *AccessDOTPermissionSet) ReduceBy(rhs *AccessDOTPermissionSet) { ps.CanList = ps.CanList && rhs.CanList } +func GetADPSFromPermString(v string) *AccessDOTPermissionSet { + ro := &AccessDOTPermissionSet{} + for len(v) > 0 { + switch v[0] { + case 'C', 'c': + ro.CanConsume = true + if len(v) > 1 && v[1] == '*' { + ro.CanConsumeStar = true + ro.CanConsumePlus = true + v = v[2:] + continue + } + if len(v) > 1 && v[1] == '+' { + ro.CanConsumePlus = true + v = v[2:] + continue + } + v = v[1:] + continue + case 'P', 'p': + ro.CanPublish = true + v = v[1:] + continue + case 'T', 't': + ro.CanTap = true + if len(v) > 1 && v[1] == '*' { + ro.CanTapStar = true + ro.CanTapPlus = true + v = v[2:] + continue + } + if len(v) > 1 && v[1] == '+' { + ro.CanTapPlus = true + v = v[2:] + continue + } + v = v[1:] + continue + case 'L', 'l': + ro.CanList = true + v = v[1:] + continue + default: + log.Infof("Hit default permstring case: %v", v[0]) + return nil + } + } + return ro +} +func (ps *AccessDOTPermissionSet) GetPermString() string { + rv := "" + if ps.CanConsumeStar { + rv += "C*" + } else if ps.CanConsumePlus { + rv += "C+" + } else if ps.CanConsume { + rv += "C" + } + if ps.CanTapStar { + rv += "T*" + } else if ps.CanTapPlus { + rv += "T+" + } else if ps.CanTap { + rv += "T" + } + if ps.CanPublish { + rv += "P" + } + if ps.CanList { + rv += "L" + } + return rv +} + //NewDOT constructs a DOT from its packed form func NewDOT(ronum int, content []byte) (rv RoutingObject, err error) { defer func() {