func queryAxo(g *gocui.Gui, to string) error { c, ok := contacts[to] if !ok { return nil } c.ratchet = goax.New(rand.Reader, privIdentity) resp, _, err := xmppClient.SendIQ(to, "get", axoQuery{}) if err != nil { debugf(g, "! Couldn't query axolotl parameters for %s: %s", to, err) } response := <-resp switch v := response.Value.(type) { case *xmpp.ClientIQ: if v.Error.Type == "cancel" { return nil } c, ok := contacts[v.From] if !ok { return nil } var q axoQuery err := xml.Unmarshal(v.Query, &q) if err != nil { debugf(g, "! Not an axolotl query: %s\n", string(v.Query)) return nil } id, err := hex.DecodeString(q.Identity) if err != nil { return err } dh, err := hex.DecodeString(q.Dh) if err != nil { return err } dh1, err := hex.DecodeString(q.Dh1) if err != nil { return err } remoteKx := &goax.KeyExchange{} copy(remoteKx.IdentityPublic[:], id) copy(remoteKx.Dh[:], dh) copy(remoteKx.Dh1[:], dh1) err = c.ratchet.CompleteKeyExchange(*remoteKx) if err != nil { debug(g, err.Error()) return nil } setContacts(g, contacts) } return nil }
func main() { // Create ratchet and kx material var priv [32]byte io.ReadFull(rand.Reader, priv[:]) ratchet := goax.New(rand.Reader, &priv) kx, err := ratchet.GetKeyExchangeMaterial() if err != nil { log.Fatal(err) } marshalled, err := json.Marshal(kx) if err != nil { log.Fatal(err) } fmt.Printf("Our kx, to be entered by remote:\n\n%s\n\n", marshalled) // Input remote kx material fmt.Println("Please enter the remote json key exchange:\n") line, err := bufio.NewReader(os.Stdin).ReadBytes(byte('\n')) if err != nil { log.Fatal(err) } var remoteKx goax.KeyExchange err = json.Unmarshal(line, &remoteKx) if err != nil { log.Fatal(err) } err = ratchet.CompleteKeyExchange(remoteKx) if err != nil { log.Fatal(err) } fmt.Println() fmt.Printf( `Great! You are now connected to %x. Type > m my message to send messages to your contact, and > g to retrieve all messages for you. Not that once retrieved, they can't be retrieved again. Type > q to quit.`, remoteKx.IdentityPublic) fmt.Println() for { fmt.Print("\n> ") cmd, msg := scan() if cmd == "g" { err := get(hex.EncodeToString(kx.IdentityPublic[:]), ratchet) if err != nil { fmt.Printf("! Error with retrieving message: %s\n", err) } } else if cmd == "m" { if msg == "" { fmt.Print("! format is 'm <message>'") continue } encrypted := hex.EncodeToString(ratchet.Encrypt([]byte(msg))) err := send(hex.EncodeToString(remoteKx.IdentityPublic[:]), encrypted) if err != nil { fmt.Printf("! Error with sending the message: %s\n", err) } } else if cmd == "q" { return } else { fmt.Println("! cmd is not understood, please enter (g)et or (m)essage or (q)uit") } } }
func main() { flag.Parse() var err error xmppClient, err = getXmppClient(*configPath) if err != nil { log.Fatal(err) } io.ReadFull(rand.Reader, privIdentity[:]) // The ui g := gocui.NewGui() if err := g.Init(); err != nil { log.Panicln(err) } defer g.Close() g.SetLayout(layout) if err := keybindings(g); err != nil { log.Panicln(err) } g.SelBgColor = gocui.ColorGreen g.SelFgColor = gocui.ColorBlack g.ShowCursor = true go func() { for { st, err := xmppClient.Next() if err != nil { debugf(g, "! Error at next stanza: %s\n", err) continue } switch v := st.Value.(type) { case *xmpp.ClientPresence: if v.From == fullJid || v.Type == "error" { continue } if len(contacts) == 0 { contacts = make(map[string]*contact) } var wantNewRatchet bool c, ok := contacts[v.From] if ok { if v.Type == "unavailable" { log.Printf("%s disconnected\n", v.From) delete(contacts, v.From) setContacts(g, contacts) } else if c.status != statusFromStatus(v.Status) { c.status = statusFromStatus(v.Status) wantNewRatchet = true } } else if v.Type != "unavailable" { contacts[v.From] = &contact{ jid: v.From, status: statusFromStatus(v.Status), } setContacts(g, contacts) wantNewRatchet = true } if wantNewRatchet { go queryAxo(g, v.From) } case *xmpp.ClientIQ: var q axoQuery err := xml.Unmarshal(v.Query, &q) if err != nil { debugf(g, "! Not an axolotl query: %s\n", string(v.Query)) continue } c, ok := contacts[v.From] if !ok { continue } if c.ratchet == nil { c.ratchet = goax.New(rand.Reader, privIdentity) } kx, err := c.ratchet.GetKeyExchangeMaterial() if err != nil { continue } resp := axoQuery{ Identity: hex.EncodeToString(kx.IdentityPublic[:]), Dh: hex.EncodeToString(kx.Dh[:]), Dh1: hex.EncodeToString(kx.Dh1[:]), } xmppClient.SendIQReply(v.From, "result", v.Id, resp) case *xmpp.ClientMessage: c, ok := contacts[v.From] if !ok || !c.HasAxo() { continue } raw, err := base64.StdEncoding.DecodeString(v.Body) if err != nil { debugf(g, "! Couldn't base64-decode: %s\n", err) continue } decrypted, err := c.ratchet.Decrypt(raw) if err != nil { debugf(g, "! Couldn't decrypt message: %s\n", err) continue } displayTimestamped(g, v.From, string(decrypted)) default: debugf(g, "! Got stanza: %v\n", st.Name) } } }() err = g.MainLoop() if err != nil && err != gocui.ErrorQuit { log.Panicln(err) } }