Example #1
0
// The main loop.
// * The main job is to pass messages between standard input/output, the OTR
// library, the TCP socket.
// * It starts goroutines that listen on standard input and the TCP socket.
// Note: it only starts listening on standard input when an encrypted
// connection has been established, to prevent any data being sent in plain
// text.
// * When an encrypted session has been established, it checks if the contact
// is authentication and authorised (according to -remember and -expect).
func mainLoop(privateKey otr.PrivateKey, upstream io.ReadWriter) {
	var conv otr.Conversation
	var theirFingerprint string = ""

	conv.PrivateKey = &privateKey

	netOutChan := make(chan []byte, 100)
	netInChan := make(chan []byte, 100)
	stdOutChan := make(chan []byte, 100)
	stdInChan := make(chan []byte, 100)
	sigTermChan := make(chan os.Signal)

	// Delimit ciphertext messages with newlines
	var nl = []byte("\n")
	msgSender, msgReceiver := NewDelimitedSender(upstream, nl), NewDelimitedReceiver(upstream, nl)

	go SendForever(msgSender, netOutChan)
	go ReceiveForever(msgReceiver, netInChan)
	// Don't touch secret input or output anything until we are sure everything
	// is encrypted and authorised.
	// go bufferedReadLoop(os.Stdin, stdInChan)
	// go writeLoop(os.Stdout, stdOutChan)
	go sigLoop(sigTermChan)

	send := func(toSend [][]byte) {
		for _, msg := range toSend {
			netOutChan <- msg
		}
	}

	stdInChan <- []byte(otr.QueryMessage) // Queue a handshake message to be sent

	authorised := false // conversation ready to send secret data?
Loop:
	for {
		select {
		case <-sigTermChan:
			break Loop

		case plaintext, alive := <-stdInChan:
			//			fmt.Fprintf(os.Stderr, "Read %d bytes of plaintext.\n", len(plaintext))
			if !alive {
				break Loop
			}
			if bytes.Index(plaintext, []byte{0}) != -1 {
				fmt.Fprintf(os.Stderr,
					"The OTR protocol only supports UTF8-encoded text.\n"+
						"Please use base64 or another suitable encoding for binary data.\n")
				break Loop
			}
			toSend, err := conv.Send(plaintext)
			if err != nil {
				exitError(err)
			}
			send(toSend)

		case otrText, alive := <-netInChan:
			if !alive {
				if authorised {
					exitPrintf("Connection dropped!  Recent messages might not be deniable.\n")
				}
				exitPrintf("Connection dropped!\n")
			}
			plaintext, encrypted, state, toSend, err := conv.Receive(otrText)
			if err != nil {
				exitError(err)
			}
			if state == otr.ConversationEnded {
				return
			}
			send(toSend)
			if conv.IsEncrypted() {
				fingerprint := string(conv.TheirPublicKey.Fingerprint())
				if authorised && theirFingerprint != fingerprint {
					exitPrintf("The contact changed mid-conversation.\n")
				}
				if !authorised {
					theirFingerprint = fingerprint
					authoriseRemember(fingerprint)
					authorised = true

					var w io.Writer
					var r io.Reader

					r, w = os.Stdin, os.Stdout
					if execCommand != "" {
						r, w = StartCommand(fingerprint)
					}
					go bufferedReadLoop(r, stdInChan)
					go writeLoop(w, stdOutChan)
				}
			}
			if len(plaintext) > 0 {
				if !encrypted || !authorised {
					exitPrintf("Received unencrypted or unauthenticated text.\n")
				}
				//				fmt.Fprintf(os.Stderr, "Received %d bytes of plaintext.\n", len(plaintext))
				stdOutChan <- plaintext
			}
		}
	}

	// We want to terminate the conversation.  To do this, we send the
	// termination messages, and wait for the other side to close the
	// connection.  It's important that these messages get through, for
	// deniability.
	toSend := conv.End()
	send(toSend)
	netOutChan <- nil
ShutdownLoop:
	for {
		select {
		case _, alive := <-netInChan:
			if !alive {
				break ShutdownLoop
			}
		}
	}
}
Example #2
0
func main() {
	flag.Parse()
	log.SetFlags(log.Ldate | log.Lmicroseconds | log.Lshortfile)

	prv := otr.PrivateKey{}
	if *genKey {
		prv.Generate(rand.Reader)
		fmt.Println(base64.RawStdEncoding.EncodeToString(prv.Serialize(nil)))
		return
	}

	if *prvDataEnc == "" {
		log.Fatalln("You have to provide OTR key")
	}
	prvData, err := base64.RawStdEncoding.DecodeString(*prvDataEnc)
	if err != nil {
		log.Fatalln("Invalid private key's Base64 encoding:", err)
	}
	if _, ok := prv.Parse(prvData); !ok {
		log.Fatalln("Unable to parse private key")
	}
	log.Println("Our fingerprint:", hex.EncodeToString(prv.Fingerprint()))

	var wg sync.WaitGroup
	var ctin *os.File
	var ctout *os.File
	var ptin *os.File
	var ptout *os.File
	opener := func(fdd **os.File, path string, flag int) {
		fd, err := os.OpenFile(path, flag, os.FileMode(0600))
		if err != nil {
			log.Fatalln("Unable to open", path, "file:", err)
		}
		*fdd = fd
		wg.Done()
	}
	wg.Add(1)
	go opener(&ctin, *ctInPath, os.O_RDONLY)
	wg.Add(1)
	go opener(&ptin, *ptInPath, os.O_RDONLY)
	wg.Add(1)
	go opener(&ctout, *ctOutPath, os.O_WRONLY)
	wg.Add(1)
	go opener(&ptout, *ptOutPath, os.O_WRONLY)

	wg.Wait()
	conv := otr.Conversation{PrivateKey: &prv, FragmentSize: MaxMsgSize}
	exited := make(chan struct{})

	go func() {
		scanner := bufio.NewScanner(ptin)
		var err error
		var msg []byte
		var msgs [][]byte
		var t string
		for scanner.Scan() {
			t = scanner.Text()
			if strings.HasPrefix(t, OTRStart) {
				ctout.Write(append([]byte(otr.QueryMessage), '\n'))
				continue
			} else if t == OTRStop {
				msgs = conv.End()
			} else if strings.HasPrefix(t, OTRSMP) {
				ss := strings.SplitN(t, " ", 3)
				var question string
				if len(ss) == 1 {
					log.Println("Invalid SMP usage")
					continue
				} else if len(ss) == 3 {
					question = ss[2]
				}
				msgs, err = conv.Authenticate(question, []byte(ss[1]))
				if err != nil {
					log.Println("Error during authentication:", err)
					continue
				}
			} else {
				msgs, err = conv.Send(scanner.Bytes())
				if err != nil {
					log.Println("Error during sending:", err)
					continue
				}
			}
			for _, msg = range msgs {
				ctout.Write(append(msg, '\n'))
			}
		}
		exited <- struct{}{}
	}()

	go func() {
		scanner := bufio.NewScanner(ctin)
		var got []byte
		var msg []byte
		var enc bool
		var change otr.SecurityChange
		var toSend [][]byte
		var err error
		for scanner.Scan() {
			got, enc, change, toSend, err = conv.Receive(scanner.Bytes())
			if err != nil {
				log.Println("Error during receiving:", err)
				continue
			}
			for _, msg = range toSend {
				ctout.Write(append(msg, '\n'))
			}
			switch change {
			case otr.NewKeys:
				log.Println(
					"OTR established, remote fingerprint:",
					hex.EncodeToString(conv.TheirPublicKey.Fingerprint()),
				)
			case otr.ConversationEnded:
				log.Println("OTR terminated")
			case otr.SMPSecretNeeded:
				log.Println("SMP requested:", conv.SMPQuestion())
			case otr.SMPComplete:
				log.Println("SMP succeeded")
			case otr.SMPFailed:
				log.Println("SMP failed")
			}
			if len(got) > 0 {
				if !enc {
					got = append([]byte("Unencrypted:"), got...)
				}
				ptout.Write(append(got, '\n'))
			}
		}
		exited <- struct{}{}
	}()

	killed := make(chan os.Signal)
	signal.Notify(killed, os.Interrupt, os.Kill)
	select {
	case <-exited:
	case <-killed:
	}
	if conv.IsEncrypted() {
		for _, msg := range conv.End() {
			ctout.Write(append(msg, '\n'))
		}
	}
	ctout.Close()
	ctin.Close()
	ptout.Close()
	ptin.Close()
}