mirror of
https://github.com/permissionlesstech/bitchat.git
synced 2026-07-25 22:05:21 +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:
@@ -18,6 +18,11 @@ struct ContentView: View {
|
||||
@State private var showSidebar = false
|
||||
@State private var sidebarDragOffset: CGFloat = 0
|
||||
@State private var showAppInfo = false
|
||||
@State private var showPasswordInput = false
|
||||
@State private var passwordInputRoom: String? = nil
|
||||
@State private var passwordInput = ""
|
||||
@State private var showPasswordPrompt = false
|
||||
@State private var passwordPromptInput = ""
|
||||
|
||||
private var backgroundColor: Color {
|
||||
colorScheme == .dark ? Color.black : Color.white
|
||||
@@ -160,6 +165,40 @@ struct ContentView: View {
|
||||
.sheet(isPresented: $showAppInfo) {
|
||||
AppInfoView()
|
||||
}
|
||||
.alert("Set Room Password", isPresented: $showPasswordInput) {
|
||||
SecureField("Password", text: $passwordInput)
|
||||
Button("Cancel", role: .cancel) {
|
||||
passwordInput = ""
|
||||
passwordInputRoom = nil
|
||||
}
|
||||
Button("Set Password") {
|
||||
if let room = passwordInputRoom, !passwordInput.isEmpty {
|
||||
viewModel.setRoomPassword(passwordInput, for: room)
|
||||
passwordInput = ""
|
||||
passwordInputRoom = nil
|
||||
}
|
||||
}
|
||||
} message: {
|
||||
Text("Enter a password to protect \(passwordInputRoom ?? "room"). Others will need this password to read messages.")
|
||||
}
|
||||
.alert("Enter Room Password", isPresented: Binding(
|
||||
get: { viewModel.showPasswordPrompt },
|
||||
set: { viewModel.showPasswordPrompt = $0 }
|
||||
)) {
|
||||
SecureField("Password", text: $passwordPromptInput)
|
||||
Button("Cancel", role: .cancel) {
|
||||
passwordPromptInput = ""
|
||||
viewModel.passwordPromptRoom = nil
|
||||
}
|
||||
Button("Join") {
|
||||
if let room = viewModel.passwordPromptRoom, !passwordPromptInput.isEmpty {
|
||||
viewModel.joinRoom(room, password: passwordPromptInput)
|
||||
passwordPromptInput = ""
|
||||
}
|
||||
}
|
||||
} message: {
|
||||
Text("This room is password protected. Enter the password to join.")
|
||||
}
|
||||
}
|
||||
|
||||
private var headerView: some View {
|
||||
@@ -517,6 +556,13 @@ struct ContentView: View {
|
||||
}
|
||||
}) {
|
||||
HStack {
|
||||
// Lock icon for password protected rooms
|
||||
if viewModel.passwordProtectedRooms.contains(room) {
|
||||
Image(systemName: "lock.fill")
|
||||
.font(.system(size: 10))
|
||||
.foregroundColor(secondaryTextColor)
|
||||
}
|
||||
|
||||
Text(room)
|
||||
.font(.system(size: 14, design: .monospaced))
|
||||
.foregroundColor(viewModel.currentRoom == room ? Color.blue : textColor)
|
||||
@@ -534,22 +580,49 @@ struct ContentView: View {
|
||||
.clipShape(Capsule())
|
||||
}
|
||||
|
||||
// Leave button
|
||||
// Room controls
|
||||
if viewModel.currentRoom == room {
|
||||
Button(action: {
|
||||
viewModel.leaveRoom(room)
|
||||
}) {
|
||||
Text("leave")
|
||||
.font(.system(size: 10, design: .monospaced))
|
||||
.foregroundColor(secondaryTextColor)
|
||||
.padding(.horizontal, 8)
|
||||
.padding(.vertical, 2)
|
||||
.overlay(
|
||||
RoundedRectangle(cornerRadius: 4)
|
||||
.stroke(secondaryTextColor.opacity(0.5), lineWidth: 1)
|
||||
)
|
||||
HStack(spacing: 4) {
|
||||
// Password button for room creator
|
||||
if viewModel.roomCreators[room] == viewModel.meshService.myPeerID {
|
||||
Button(action: {
|
||||
// Toggle password protection
|
||||
if viewModel.passwordProtectedRooms.contains(room) {
|
||||
viewModel.removeRoomPassword(for: room)
|
||||
} else {
|
||||
// Show password input
|
||||
showPasswordInput = true
|
||||
passwordInputRoom = room
|
||||
}
|
||||
}) {
|
||||
Image(systemName: viewModel.passwordProtectedRooms.contains(room) ? "lock.open" : "lock")
|
||||
.font(.system(size: 10))
|
||||
.foregroundColor(secondaryTextColor)
|
||||
.padding(4)
|
||||
.overlay(
|
||||
RoundedRectangle(cornerRadius: 4)
|
||||
.stroke(secondaryTextColor.opacity(0.5), lineWidth: 1)
|
||||
)
|
||||
}
|
||||
.buttonStyle(.plain)
|
||||
}
|
||||
|
||||
// Leave button
|
||||
Button(action: {
|
||||
viewModel.leaveRoom(room)
|
||||
}) {
|
||||
Text("leave")
|
||||
.font(.system(size: 10, design: .monospaced))
|
||||
.foregroundColor(secondaryTextColor)
|
||||
.padding(.horizontal, 8)
|
||||
.padding(.vertical, 2)
|
||||
.overlay(
|
||||
RoundedRectangle(cornerRadius: 4)
|
||||
.stroke(secondaryTextColor.opacity(0.5), lineWidth: 1)
|
||||
)
|
||||
}
|
||||
.buttonStyle(.plain)
|
||||
}
|
||||
.buttonStyle(.plain)
|
||||
}
|
||||
}
|
||||
.padding(.horizontal, 12)
|
||||
|
||||
Reference in New Issue
Block a user