mirror of
https://github.com/permissionlesstech/bitchat.git
synced 2026-07-25 19:05:20 +00:00
Implement password-protected rooms with encryption
- Room creators can set/remove passwords for their rooms - Passwords are used to derive encryption keys via PBKDF2 - All messages in password-protected rooms are encrypted with AES-GCM - Room protection status is announced to all peers - Password and room data persists across app launches - Visual lock indicators for password-protected rooms - Password prompts when joining protected rooms - Only room creators can modify password settings - Encrypted messages show placeholder text if password is unknown
This commit is contained in:
@@ -180,14 +180,14 @@ extension BitchatMessage {
|
||||
var data = Data()
|
||||
|
||||
// Message format:
|
||||
// - Flags: 1 byte (bit 0: isRelay, bit 1: isPrivate, bit 2: hasOriginalSender, bit 3: hasRecipientNickname, bit 4: hasSenderPeerID, bit 5: hasMentions, bit 6: hasVoiceNote)
|
||||
// - Flags: 1 byte (bit 0: isRelay, bit 1: isPrivate, bit 2: hasOriginalSender, bit 3: hasRecipientNickname, bit 4: hasSenderPeerID, bit 5: hasMentions, bit 6: hasRoom, bit 7: isEncrypted)
|
||||
// - Timestamp: 8 bytes (seconds since epoch)
|
||||
// - ID length: 1 byte
|
||||
// - ID: variable
|
||||
// - Sender length: 1 byte
|
||||
// - Sender: variable
|
||||
// - Content length: 2 bytes
|
||||
// - Content: variable
|
||||
// - Content: variable (or encrypted content if isEncrypted)
|
||||
// Optional fields based on flags:
|
||||
// - Original sender length + data
|
||||
// - Recipient nickname length + data
|
||||
@@ -203,6 +203,7 @@ extension BitchatMessage {
|
||||
if senderPeerID != nil { flags |= 0x10 }
|
||||
if mentions != nil && !mentions!.isEmpty { flags |= 0x20 }
|
||||
if room != nil { flags |= 0x40 }
|
||||
if isEncrypted { flags |= 0x80 }
|
||||
|
||||
data.append(flags)
|
||||
|
||||
@@ -229,8 +230,14 @@ extension BitchatMessage {
|
||||
data.append(0)
|
||||
}
|
||||
|
||||
// Content
|
||||
if let contentData = content.data(using: .utf8) {
|
||||
// Content or encrypted content
|
||||
if isEncrypted, let encryptedContent = encryptedContent {
|
||||
let length = UInt16(min(encryptedContent.count, 65535))
|
||||
// Encode length as 2 bytes, big-endian
|
||||
data.append(UInt8((length >> 8) & 0xFF))
|
||||
data.append(UInt8(length & 0xFF))
|
||||
data.append(encryptedContent.prefix(Int(length)))
|
||||
} else if let contentData = content.data(using: .utf8) {
|
||||
let length = UInt16(min(contentData.count, 65535))
|
||||
// Encode length as 2 bytes, big-endian
|
||||
data.append(UInt8((length >> 8) & 0xFF))
|
||||
@@ -301,6 +308,7 @@ extension BitchatMessage {
|
||||
let hasSenderPeerID = (flags & 0x10) != 0
|
||||
let hasMentions = (flags & 0x20) != 0
|
||||
let hasRoom = (flags & 0x40) != 0
|
||||
let isEncrypted = (flags & 0x80) != 0
|
||||
|
||||
// Timestamp
|
||||
guard offset + 8 <= dataCopy.count else {
|
||||
@@ -347,7 +355,19 @@ extension BitchatMessage {
|
||||
guard offset + contentLength <= dataCopy.count else {
|
||||
return nil
|
||||
}
|
||||
let content = String(data: dataCopy[offset..<offset+contentLength], encoding: .utf8) ?? ""
|
||||
|
||||
let content: String
|
||||
let encryptedContent: Data?
|
||||
|
||||
if isEncrypted {
|
||||
// Content is encrypted, store as Data
|
||||
encryptedContent = dataCopy[offset..<offset+contentLength]
|
||||
content = "" // Empty placeholder
|
||||
} else {
|
||||
// Normal string content
|
||||
content = String(data: dataCopy[offset..<offset+contentLength], encoding: .utf8) ?? ""
|
||||
encryptedContent = nil
|
||||
}
|
||||
offset += contentLength
|
||||
|
||||
// Optional fields
|
||||
@@ -418,7 +438,9 @@ extension BitchatMessage {
|
||||
recipientNickname: recipientNickname,
|
||||
senderPeerID: senderPeerID,
|
||||
mentions: mentions,
|
||||
room: room
|
||||
room: room,
|
||||
encryptedContent: encryptedContent,
|
||||
isEncrypted: isEncrypted
|
||||
)
|
||||
return message
|
||||
}
|
||||
|
||||
@@ -69,6 +69,7 @@ enum MessageType: UInt8 {
|
||||
case fragmentStart = 0x05
|
||||
case fragmentContinue = 0x06
|
||||
case fragmentEnd = 0x07
|
||||
case roomAnnounce = 0x08 // Announce password-protected room status
|
||||
}
|
||||
|
||||
// Special recipient ID for broadcast messages
|
||||
@@ -134,8 +135,10 @@ struct BitchatMessage: Codable, Equatable {
|
||||
let senderPeerID: String?
|
||||
let mentions: [String]? // Array of mentioned nicknames
|
||||
let room: String? // Room hashtag (e.g., "#general")
|
||||
let encryptedContent: Data? // For password-protected rooms
|
||||
let isEncrypted: Bool // Flag to indicate if content is encrypted
|
||||
|
||||
init(sender: String, content: String, timestamp: Date, isRelay: Bool, originalSender: String? = nil, isPrivate: Bool = false, recipientNickname: String? = nil, senderPeerID: String? = nil, mentions: [String]? = nil, room: String? = nil) {
|
||||
init(sender: String, content: String, timestamp: Date, isRelay: Bool, originalSender: String? = nil, isPrivate: Bool = false, recipientNickname: String? = nil, senderPeerID: String? = nil, mentions: [String]? = nil, room: String? = nil, encryptedContent: Data? = nil, isEncrypted: Bool = false) {
|
||||
self.id = UUID().uuidString
|
||||
self.sender = sender
|
||||
self.content = content
|
||||
@@ -147,6 +150,8 @@ struct BitchatMessage: Codable, Equatable {
|
||||
self.senderPeerID = senderPeerID
|
||||
self.mentions = mentions
|
||||
self.room = room
|
||||
self.encryptedContent = encryptedContent
|
||||
self.isEncrypted = isEncrypted
|
||||
}
|
||||
}
|
||||
|
||||
@@ -156,6 +161,8 @@ protocol BitchatDelegate: AnyObject {
|
||||
func didDisconnectFromPeer(_ peerID: String)
|
||||
func didUpdatePeerList(_ peers: [String])
|
||||
func didReceiveRoomLeave(_ room: String, from peerID: String)
|
||||
func didReceivePasswordProtectedRoomAnnouncement(_ room: String, isProtected: Bool, creatorID: String?)
|
||||
func decryptRoomMessage(_ encryptedContent: Data, room: String) -> String?
|
||||
|
||||
// Optional method to check if a fingerprint belongs to a favorite peer
|
||||
func isFavorite(fingerprint: String) -> Bool
|
||||
@@ -170,4 +177,13 @@ extension BitchatDelegate {
|
||||
func didReceiveRoomLeave(_ room: String, from peerID: String) {
|
||||
// Default empty implementation
|
||||
}
|
||||
|
||||
func didReceivePasswordProtectedRoomAnnouncement(_ room: String, isProtected: Bool, creatorID: String?) {
|
||||
// Default empty implementation
|
||||
}
|
||||
|
||||
func decryptRoomMessage(_ encryptedContent: Data, room: String) -> String? {
|
||||
// Default returns nil (unable to decrypt)
|
||||
return nil
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user