178 lines
3.4 KiB
Go
178 lines
3.4 KiB
Go
package peer
|
|
|
|
import (
|
|
"crypto/rsa"
|
|
"fmt"
|
|
"strings"
|
|
|
|
"github.com/rs/zerolog/log"
|
|
|
|
"github.com/awnumar/memguard"
|
|
|
|
"git.tcp.direct/tcp.direct/IR5EC/chat"
|
|
"git.tcp.direct/tcp.direct/IR5EC/connection"
|
|
"git.tcp.direct/tcp.direct/IR5EC/crypto"
|
|
)
|
|
|
|
type Peer struct {
|
|
ID string
|
|
Name string
|
|
|
|
Conn *connection.Session
|
|
CleartextInbox []*chat.Msg
|
|
|
|
decryptionKey *memguard.Enclave
|
|
PublicKey *rsa.PublicKey
|
|
|
|
trusted bool
|
|
handshaking bool
|
|
}
|
|
|
|
func (p *Peer) DecryptionKey() []byte {
|
|
o, err := p.decryptionKey.Open()
|
|
if err != nil {
|
|
log.Warn().Str("caller", p.Name).Msg(err.Error())
|
|
return nil
|
|
}
|
|
o.RLock()
|
|
defer o.RUnlock()
|
|
return o.Bytes()
|
|
}
|
|
|
|
func (p *Peer) RSAEncrypt(buf string) string {
|
|
return crypto.RSAEncrypt(p.PublicKey, buf)
|
|
}
|
|
|
|
func (p *Peer) decryptPrivMSG(m *chat.Msg) {
|
|
msgIndex := m.Offset + 2
|
|
|
|
msg := strings.Join(m.Components[msgIndex:], " ")
|
|
trimmedComponents := m.Components[:msgIndex]
|
|
|
|
plaintext := true
|
|
|
|
// Extract Message
|
|
msg = msg[1 : len(msg)-2]
|
|
|
|
// Check if RSA Message
|
|
if strings.HasPrefix(msg, "RSA ") {
|
|
msg = p.Conn.rsaDecrypt(msg)
|
|
plaintext = false
|
|
}
|
|
|
|
// Check for non encrypted control command
|
|
if strings.HasPrefix(msg, "CTRL ") {
|
|
p.recieveControlCommand(msg)
|
|
return
|
|
}
|
|
|
|
// Determine if we can decrypt or if we have to request the decryption key
|
|
if !p.canDecrypt() {
|
|
p.beginHandshake()
|
|
p.CleartextInbox = append(p.CleartextInbox, m)
|
|
return
|
|
}
|
|
|
|
// Decrypt Message
|
|
decMsg := p.aesDecryptString(msg)
|
|
if decMsg != msg {
|
|
plaintext = false
|
|
}
|
|
|
|
msg = decMsg
|
|
|
|
// Check for encrypted control command
|
|
if strings.HasPrefix(msg, "CTRL ") {
|
|
p.recieveControlCommand(msg)
|
|
return
|
|
}
|
|
|
|
// Properly Name Sender
|
|
m.senderComponents[0] = p.Name
|
|
|
|
sender := strings.Join(m.senderComponents, "!~")
|
|
trimmedComponents[0] = ":" + sender
|
|
|
|
// Add cleartext warning
|
|
if plaintext {
|
|
msg += " [SENT AS CLEARTEXT]"
|
|
}
|
|
|
|
// Rebuild Message string
|
|
result := strings.Join(trimmedComponents, " ") + " :" + msg + "\n"
|
|
|
|
p.Conn.writeClient([]byte(result))
|
|
}
|
|
|
|
func (p *Peer) canDecrypt() bool {
|
|
return p.decryptionKey != nil
|
|
}
|
|
|
|
//
|
|
// Handshakes
|
|
//
|
|
|
|
func (p *Peer) beginHandshake() {
|
|
p.sendControlCommand("HANDSHAKE", p.Conn.publicKeyBase64())
|
|
p.handshaking = true
|
|
}
|
|
|
|
//
|
|
// Control Commands
|
|
//
|
|
|
|
// Basics
|
|
func (p *Peer) sendControlCommand(command string, payload string) {
|
|
msg := "CTRL " + command
|
|
|
|
if payload != "" {
|
|
msg += " " + payload
|
|
}
|
|
|
|
p.sendPrivateMessage(msg)
|
|
}
|
|
|
|
func (p *Peer) sendControlCommandRSA(command string, payload string) {
|
|
msg := "CTRL " + command
|
|
|
|
if payload != "" {
|
|
msg += " " + payload
|
|
}
|
|
|
|
msg = p.rsaEncrypt(msg)
|
|
|
|
p.sendPrivateMessage(msg)
|
|
}
|
|
|
|
func (p *Peer) sendPrivateMessage(message string) {
|
|
msg := "PRIVMSG " + p.ID + " :" + message
|
|
p.Conn.writeServer([]byte(msg + "\n"))
|
|
}
|
|
|
|
//
|
|
// After the handshake, decrypt the messages that were sent previously
|
|
//
|
|
func (p *Peer) decryptUnencryptedMessages() {
|
|
if p.canDecrypt() {
|
|
for _, message := range p.CleartextInbox {
|
|
message.decrypt()
|
|
}
|
|
|
|
p.CleartextInbox = []*message{}
|
|
}
|
|
}
|
|
|
|
func (p *Peer) displayName() string {
|
|
fingerprintHex := p.publicKeyFingerprintSha1()
|
|
fingerprintChunks := make([]string, len(fingerprintHex)/2)
|
|
for i := range fingerprintChunks {
|
|
fingerprintChunks[i] = string(fingerprintHex[(i*2)]) + string(fingerprintHex[(i*2)+1])
|
|
}
|
|
|
|
fingerprint := strings.Join(fingerprintChunks, ":")
|
|
|
|
fmt.Println(fingerprint, fingerprintHex)
|
|
|
|
return p.Name + " (" + fingerprint + ")"
|
|
}
|