diff --git a/src/app/spreed-webrtc-server/channelling.go b/src/app/spreed-webrtc-server/channelling.go index c37629baa..f3c1675b6 100644 --- a/src/app/spreed-webrtc-server/channelling.go +++ b/src/app/spreed-webrtc-server/channelling.go @@ -195,6 +195,11 @@ type DataIncoming struct { Sessions *DataSessions Room *DataRoom Iid string `json:",omitempty"` + + EncryptionRegister *DataEncryptionRegister + EncryptionRequestKeyBundle *DataEncryptionRequestKeyBundle + EncryptionKeyBundle *DataEncryptionKeyBundle + Encrypted *DataEncrypted } type DataOutgoing struct { @@ -231,3 +236,38 @@ type DataAuthentication struct { Type string Authentication *SessionToken } + +type DataEncryptionRegisterSignedPreKey struct { + Id int64 + Key string + Signature string +} + +type DataEncryptionRegister struct { + RegistrationId int64 + Identity string + LastResortSignedPreKey DataEncryptionRegisterSignedPreKey +} + +type DataEncryptionRequestKeyBundle struct { + To string `json:",omitempty"` + Type string `json:",omitempty"` +} + +type DataEncryptionKeyBundle struct { + To string `json:",omitempty"` + Type string `json:",omitempty"` + Identity string + PreKeyId int64 `json:",omitempty"` + PreKey string `json:",omitempty"` + SignedPreKeyId int64 `json:",omitempty"` + SignedPreKey string `json:",omitempty"` + SignedPreKeySignature string `json:",omitempty"` +} + +type DataEncrypted struct { + To string `json:",omitempty"` + Type string `json:",omitempty"` + Message string + Data string +} diff --git a/src/app/spreed-webrtc-server/channelling_api.go b/src/app/spreed-webrtc-server/channelling_api.go index f44e4671e..1ca775ee2 100644 --- a/src/app/spreed-webrtc-server/channelling_api.go +++ b/src/app/spreed-webrtc-server/channelling_api.go @@ -156,6 +156,30 @@ func (api *channellingAPI) OnIncoming(sender Sender, session *Session, msg *Data } return api.HandleRoom(session, msg.Room) + case "EncryptionRegister": + if msg.EncryptionRegister == nil { + return nil, NewDataError("bad_request", "message did not contain EncryptionRegister") + } + + api.HandleEncryptionRegister(session, msg.EncryptionRegister) + case "EncryptionRequestKeyBundle": + if msg.EncryptionRequestKeyBundle == nil { + return nil, NewDataError("bad_request", "message did not contain EncryptionRequestKeyBundle") + } + + return api.HandleEncryptionRequestKeyBundle(session, msg.EncryptionRequestKeyBundle) + case "EncryptionKeyBundle": + if msg.EncryptionKeyBundle == nil { + return nil, NewDataError("bad_request", "message did not contain EncryptionKeyBundle") + } + + return api.HandleEncryptionKeyBundle(session, msg.EncryptionKeyBundle) + case "Encrypted": + if msg.Encrypted == nil { + return nil, NewDataError("bad_request", "message did not contain Encrypted") + } + + return api.HandleEncrypted(session, msg.Encrypted) default: log.Println("OnText unhandled message type", msg.Type) } @@ -327,3 +351,42 @@ func (api *channellingAPI) HandleRoom(session *Session, room *DataRoom) (*DataRo } return room, err } + +func (api *channellingAPI) HandleEncryptionRegister(session *Session, register *DataEncryptionRegister) { + session.encryptionRegistration = register +} + +func (api *channellingAPI) HandleEncryptionRequestKeyBundle(session *Session, request *DataEncryptionRequestKeyBundle) (interface{}, error) { + if request.To == "" { + return nil, NewDataError("empty_peer", "cannot send to empty peer") + } + // TODO(fancycode): Check if peer is online and return bundle based on + // registration data if not. + message := &DataEncryptionRequestKeyBundle{ + Type: "EncryptionRequestKeyBundle", + } + session.Unicast(request.To, message) + return nil, nil +} + +func (api *channellingAPI) HandleEncryptionKeyBundle(session *Session, bundle *DataEncryptionKeyBundle) (interface{}, error) { + if bundle.To == "" { + return nil, NewDataError("empty_peer", "cannot send to empty peer") + } + message := *bundle + message.To = "" + message.Type = "EncryptionKeyBundle" + session.Unicast(bundle.To, message) + return nil, nil +} + +func (api *channellingAPI) HandleEncrypted(session *Session, data *DataEncrypted) (interface{}, error) { + if data.To == "" { + return nil, NewDataError("empty_peer", "cannot send to empty peer") + } + message := *data + message.To = "" + message.Type = "Encrypted" + session.Unicast(data.To, message) + return nil, nil +} diff --git a/src/app/spreed-webrtc-server/session.go b/src/app/spreed-webrtc-server/session.go index 84043147d..07372ee0b 100644 --- a/src/app/spreed-webrtc-server/session.go +++ b/src/app/spreed-webrtc-server/session.go @@ -56,6 +56,8 @@ type Session struct { subscribers map[string]*Session disconnected bool replaced bool + + encryptionRegistration *DataEncryptionRegister } func NewSession(manager SessionManager, unicaster Unicaster, broadcaster Broadcaster, rooms RoomStatusManager, buddyImages ImageCache, attestations *securecookie.SecureCookie, id, sid string) *Session { diff --git a/src/styles/components/_chat.scss b/src/styles/components/_chat.scss index 4cbb8f885..a1e5d6b4c 100644 --- a/src/styles/components/_chat.scss +++ b/src/styles/components/_chat.scss @@ -220,6 +220,19 @@ } } + .identityhint { + color: $chat-typing; + font-size: .8em; + height: 16px; + overflow: hidden; + padding: 0 6px; + white-space: nowrap; + + @include breakpt($breakpoint-chat-small, max-height) { + display: none; + } + } + .inputbox { position: relative; @@ -349,6 +362,15 @@ width: 12px; } + &:after { + float: right; + font-family: FontAwesome; + left: 0; + margin-left: 1em; + text-align: center; + width: 12px; + } + &.unread:before { color: $chat-msg-unread-icon-color; content: $chat-msg-unread-icon; @@ -378,6 +400,11 @@ color: $chat-msg-read-icon-color; content: $chat-msg-read-icon; } + + &.encrypted:after { + color: $chat-msg-encrypted-icon-color; + content: $chat-msg-encrypted-icon; + } } .buddyPicture { diff --git a/src/styles/global/_variables.scss b/src/styles/global/_variables.scss index 4c7b374b6..c93cd8e60 100644 --- a/src/styles/global/_variables.scss +++ b/src/styles/global/_variables.scss @@ -123,6 +123,7 @@ $chat-msg-sent-icon-color: #5882fa !default; $chat-msg-delivered-icon-color: #5882fa !default; $chat-msg-received-icon-color: #84b819 !default; $chat-msg-read-icon-color: $chat-msg-default-icon-color !default; +$chat-msg-encrypted-icon-color: $chat-msg-default-icon-color !default; $chat-msg-unread-icon: '\f0eb' !default; $chat-msg-sending-icon: '\f0ec' !default; @@ -130,6 +131,7 @@ $chat-msg-sent-icon: '\f003' !default; $chat-msg-delivered-icon: '\f019' !default; $chat-msg-received-icon: '\f06e' !default; $chat-msg-read-icon: '\f00c' !default; +$chat-msg-encrypted-icon: '\f023' !default; $chat-msg-self-background: #fff !default; $chat-msg-self-color: $font-color !default; diff --git a/static/js/controllers/chatroomcontroller.js b/static/js/controllers/chatroomcontroller.js index d6611ef85..c815e13bb 100644 --- a/static/js/controllers/chatroomcontroller.js +++ b/static/js/controllers/chatroomcontroller.js @@ -214,7 +214,7 @@ define(['jquery', 'underscore', 'moment', 'text!partials/fileinfo.html', 'text!p } - $scope.display = function(s, nodes, extra_css, title, picture) { + $scope.display = function(s, nodes, extra_css, title, picture, encrypted) { var container; var element; @@ -235,6 +235,9 @@ define(['jquery', 'underscore', 'moment', 'text!partials/fileinfo.html', 'text!p lastMessageContainer = $("