Example #1
0
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
}
Example #2
0
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")
		}
	}
}
Example #3
0
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)
	}
}