mirror of
https://github.com/comma-hacks/webrtc.git
synced 2025-09-27 04:26:31 +08:00
260 lines
6.6 KiB
Go
260 lines
6.6 KiB
Go
package secureput
|
|
|
|
// https://gist.github.com/locked/b066aa1ddeb2b:e855e
|
|
|
|
import (
|
|
"bytes"
|
|
"encoding/json"
|
|
"log"
|
|
"strings"
|
|
|
|
"github.com/gorilla/websocket"
|
|
"github.com/pion/webrtc/v3"
|
|
)
|
|
|
|
const (
|
|
Identified = iota
|
|
Connected
|
|
CloseError
|
|
UnexpectedCloseError
|
|
ConnectionTimeout
|
|
)
|
|
|
|
var peerConnection *webrtc.PeerConnection
|
|
|
|
func (ap *SecurePut) SignalMessageHandler(message *Message, signalClient *websocket.Conn) {
|
|
|
|
switch message.PayloadType {
|
|
case SessionDescriptionType:
|
|
var err error
|
|
|
|
if peerConnection != nil {
|
|
peerConnection.Close()
|
|
peerConnection = nil
|
|
}
|
|
|
|
// Prepare the configuration
|
|
var webrtcConfig = webrtc.Configuration{
|
|
ICEServers: []webrtc.ICEServer{
|
|
{
|
|
URLs: StunServers,
|
|
},
|
|
},
|
|
}
|
|
|
|
if peerConnection, err = webrtc.NewPeerConnection(webrtcConfig); err != nil {
|
|
log.Println("new peer connection -> error:", err)
|
|
return
|
|
}
|
|
|
|
var offer webrtc.SessionDescription
|
|
if err := json.Unmarshal(message.PayloadContent, &offer); err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
log.Println("got offer")
|
|
|
|
if err = peerConnection.SetRemoteDescription(offer); err != nil {
|
|
panic(err)
|
|
}
|
|
|
|
answer, err := peerConnection.CreateAnswer(nil)
|
|
if err != nil {
|
|
panic(err)
|
|
} else if err = peerConnection.SetLocalDescription(answer); err != nil {
|
|
panic(err)
|
|
}
|
|
|
|
ap.EncryptingWriteJSON(signalClient, SDPMessage{
|
|
PayloadType: SessionDescriptionType,
|
|
PayloadContent: *peerConnection.LocalDescription(),
|
|
})
|
|
|
|
log.Println("sent a reply")
|
|
|
|
// Set the handler for ICE connection state
|
|
// This will notify you when the peer has connected/disconnected
|
|
peerConnection.OnICEConnectionStateChange(func(connectionState webrtc.ICEConnectionState) {
|
|
log.Printf("ICE Connection State has changed: %s\n", connectionState.String())
|
|
})
|
|
|
|
peerConnection.OnDataChannel(func(d *webrtc.DataChannel) {
|
|
d.OnOpen(func() {
|
|
log.Println("peerconn data channel open")
|
|
})
|
|
d.OnMessage(func(msg webrtc.DataChannelMessage) {
|
|
var message Message
|
|
err := json.Unmarshal(msg.Data, &message)
|
|
if err != nil {
|
|
log.Println("data channel message unmarshall error:", err)
|
|
return
|
|
}
|
|
switch message.PayloadType {
|
|
case WrappedType:
|
|
var wp WrappedPayload
|
|
if err := json.Unmarshal(message.PayloadContent, &wp); err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
data, err := decrypt([]byte(ap.Config.DeviceSecret), wp.Data)
|
|
if err != nil {
|
|
log.Println("failed to decrypt")
|
|
log.Println(err)
|
|
return
|
|
}
|
|
var msg Message
|
|
decoder := json.NewDecoder(strings.NewReader(string(data)))
|
|
if err = decoder.Decode(&msg); err != nil {
|
|
log.Println("failed to decode decrypted message", string(data))
|
|
log.Println(err)
|
|
return
|
|
}
|
|
switch msg.PayloadType {
|
|
default:
|
|
log.Println("unexpected unwrapped message on data channel")
|
|
}
|
|
default:
|
|
log.Println("unexpected message on data channel")
|
|
}
|
|
})
|
|
})
|
|
|
|
peerConnection.OnICECandidate(func(i *webrtc.ICECandidate) {
|
|
if i != nil {
|
|
// check(offerPC.AddICECandidate(i.ToJSON()))
|
|
ic := i.ToJSON()
|
|
ap.EncryptingWriteJSON(signalClient, ICEMessage{
|
|
PayloadType: IceCandidateType,
|
|
PayloadContent: ICECandidateInit{
|
|
SDP: ic.Candidate,
|
|
SDPMid: ic.SDPMid,
|
|
SDPMLineIndex: ic.SDPMLineIndex,
|
|
},
|
|
})
|
|
log.Println("sent ice candidate")
|
|
}
|
|
})
|
|
|
|
case IceCandidateType:
|
|
var ic ICECandidateInit
|
|
if err := json.Unmarshal(message.PayloadContent, &ic); err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
log.Println("got ice candidate")
|
|
if peerConnection != nil {
|
|
peerConnection.AddICECandidate(webrtc.ICECandidateInit{
|
|
Candidate: ic.SDP,
|
|
SDPMid: ic.SDPMid,
|
|
SDPMLineIndex: ic.SDPMLineIndex,
|
|
})
|
|
}
|
|
case WrappedType:
|
|
var wp WrappedPayload
|
|
if err := json.Unmarshal(message.PayloadContent, &wp); err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
data, err := decrypt([]byte(ap.Config.DeviceSecret), wp.Data)
|
|
if err != nil {
|
|
log.Println("failed to decrypt")
|
|
log.Println(err)
|
|
return
|
|
}
|
|
var msg Message
|
|
decoder := json.NewDecoder(strings.NewReader(string(data)))
|
|
if err = decoder.Decode(&msg); err != nil {
|
|
// user revoked access
|
|
log.Println("Received a message that could not be decrypted. Sender does not have the new key.")
|
|
return
|
|
}
|
|
ap.SignalMessageHandler(&msg, signalClient)
|
|
case ClaimType:
|
|
var claim ClaimPayload
|
|
if err := json.Unmarshal(message.PayloadContent, &claim); err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
log.Printf("validated a claim from %s\n", claim.AccountUUID)
|
|
ap.PairChannel <- claim.AccountUUID
|
|
signalClient.WriteJSON(IdentificationMessage{
|
|
PayloadType: IdentificationType,
|
|
PayloadContent: IdentificationPayload{
|
|
Name: ap.GetName(),
|
|
DeviceUUID: ap.Config.DeviceUUID,
|
|
AccountUUID: claim.AccountUUID,
|
|
},
|
|
})
|
|
default:
|
|
log.Printf("Whoops. unhandled message type %s\n", message.PayloadType)
|
|
}
|
|
}
|
|
|
|
func (ap *SecurePut) EncryptingWriteJSON(c *websocket.Conn, v interface{}) error {
|
|
msg := ForwardWrappedMessage{}
|
|
msg.To = ap.Config.AccountUUID
|
|
msg.Type = ForwardWrappedType
|
|
w, err := c.NextWriter(websocket.TextMessage)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
// Encode the plaintext to a string
|
|
buf := new(bytes.Buffer)
|
|
err0 := json.NewEncoder(buf).Encode(v)
|
|
if err0 != nil {
|
|
return err0
|
|
}
|
|
ciphertext, err3 := encrypt([]byte(ap.Config.DeviceSecret), buf.Bytes())
|
|
if err3 != nil {
|
|
return err3
|
|
}
|
|
// install the ciphertext into the payload
|
|
msg.Body = ciphertext
|
|
|
|
// encode as json and write to socket
|
|
err1 := json.NewEncoder(w).Encode(msg)
|
|
err2 := w.Close()
|
|
if err1 != nil {
|
|
return err1
|
|
}
|
|
return err2
|
|
}
|
|
|
|
func (ap *SecurePut) Signal() {
|
|
u := SignalServer
|
|
var signalClient *websocket.Conn
|
|
for signalClient == nil {
|
|
log.Printf("connecting to %s", u.String())
|
|
c, _, err := websocket.DefaultDialer.Dial(u.String(), nil)
|
|
if err != nil {
|
|
ap.SignalStatusChannel <- ConnectionTimeout
|
|
return
|
|
}
|
|
signalClient = c
|
|
ap.SignalStatusChannel <- Connected
|
|
|
|
signalClient.WriteJSON(IdentificationMessage{
|
|
PayloadType: IdentificationType,
|
|
PayloadContent: IdentificationPayload{
|
|
Name: ap.GetName(),
|
|
DeviceUUID: ap.Config.DeviceUUID,
|
|
AccountUUID: ap.Config.AccountUUID,
|
|
},
|
|
})
|
|
ap.SignalStatusChannel <- Identified
|
|
}
|
|
|
|
defer signalClient.Close()
|
|
|
|
for {
|
|
var message Message
|
|
if err := signalClient.ReadJSON(&message); err != nil {
|
|
log.Println("recv error:", err)
|
|
if websocket.IsCloseError(err) {
|
|
ap.SignalStatusChannel <- CloseError
|
|
return
|
|
}
|
|
if websocket.IsUnexpectedCloseError(err) {
|
|
ap.SignalStatusChannel <- UnexpectedCloseError
|
|
return
|
|
}
|
|
}
|
|
ap.SignalMessageHandler(&message, signalClient)
|
|
}
|
|
}
|