mirror of
https://github.com/neon443/ShhShell.git
synced 2026-03-11 13:26:16 +00:00
fix picker on connectionview
using trimmingchars whitespaces and newlines to remove any trailing "\n"s added padding to the button publickey data uses a switch now reorganised keymanager and used //MARKs added header into KeyType for easier expansion of key types in the future improved default key comment to use "at" between chunks and exclude time
This commit is contained in:
@@ -46,11 +46,49 @@ class KeyManager: ObservableObject {
|
|||||||
for keypair in keypairs {
|
for keypair in keypairs {
|
||||||
saveToKeychain(keypair)
|
saveToKeychain(keypair)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func renameKey(keypair: Keypair, newName: String) {
|
||||||
|
guard !newName.isEmpty else { return }
|
||||||
|
let keyID = keypair.id
|
||||||
|
guard let index = keypairs.firstIndex(where: { $0.id == keyID }) else { return }
|
||||||
|
var keypairWithNewName = keypair
|
||||||
|
keypairWithNewName.name = newName
|
||||||
|
withAnimation { keypairs[index] = keypairWithNewName }
|
||||||
|
saveKeypairs()
|
||||||
|
}
|
||||||
|
|
||||||
|
func deleteKey(_ keypair: Keypair) {
|
||||||
|
removeFromKeycahin(keypair: keypair)
|
||||||
|
let keyID = keypair.id
|
||||||
|
withAnimation { keypairs.removeAll(where: { $0.id == keyID }) }
|
||||||
|
saveKeypairs()
|
||||||
|
}
|
||||||
|
|
||||||
|
func importKey(type: KeyType, priv: String, name: String) {
|
||||||
|
if type == .ed25519 {
|
||||||
|
guard let importedKeypair = KeyManager.importSSHPrivkey(priv: priv) else { return }
|
||||||
|
saveToKeychain(importedKeypair)
|
||||||
|
} else { fatalError() }
|
||||||
|
}
|
||||||
|
|
||||||
|
func generateKey(type: KeyType, comment: String) {
|
||||||
|
switch type {
|
||||||
|
case .ed25519:
|
||||||
|
let keypair = Keypair(
|
||||||
|
type: .ed25519,
|
||||||
|
name: comment,
|
||||||
|
privateKey: Curve25519.Signing.PrivateKey().rawRepresentation
|
||||||
|
)
|
||||||
|
saveToKeychain(keypair)
|
||||||
|
}
|
||||||
loadKeypairs()
|
loadKeypairs()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//MARK: keychain
|
||||||
func saveToKeychain(_ keypair: Keypair) {
|
func saveToKeychain(_ keypair: Keypair) {
|
||||||
if keypair.type == .ed25519 {
|
switch keypair.type {
|
||||||
|
case .ed25519:
|
||||||
let curve25519 = try! Curve25519.Signing.PrivateKey(rawRepresentation: keypair.privateKey)
|
let curve25519 = try! Curve25519.Signing.PrivateKey(rawRepresentation: keypair.privateKey)
|
||||||
let readKey: Curve25519.Signing.PrivateKey?
|
let readKey: Curve25519.Signing.PrivateKey?
|
||||||
readKey = try! passwordStore.readKey(account: keypair.id.uuidString)
|
readKey = try! passwordStore.readKey(account: keypair.id.uuidString)
|
||||||
@@ -58,8 +96,10 @@ class KeyManager: ObservableObject {
|
|||||||
try! passwordStore.deleteKey(account: keypair.id.uuidString)
|
try! passwordStore.deleteKey(account: keypair.id.uuidString)
|
||||||
}
|
}
|
||||||
try! passwordStore.storeKey(curve25519.genericKeyRepresentation, account: keypair.id.uuidString)
|
try! passwordStore.storeKey(curve25519.genericKeyRepresentation, account: keypair.id.uuidString)
|
||||||
} else {
|
}
|
||||||
|
if !keypairs.contains(keypair) {
|
||||||
|
keypairs.append(keypair)
|
||||||
|
saveKeypairs()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -97,46 +137,7 @@ class KeyManager: ObservableObject {
|
|||||||
saveKeypairs()
|
saveKeypairs()
|
||||||
}
|
}
|
||||||
|
|
||||||
func renameKey(keypair: Keypair, newName: String) {
|
//MARK: openssh converters/importers
|
||||||
guard !newName.isEmpty else { return }
|
|
||||||
let keyID = keypair.id
|
|
||||||
guard let index = keypairs.firstIndex(where: { $0.id == keyID }) else { return }
|
|
||||||
var keypairWithNewName = keypair
|
|
||||||
keypairWithNewName.name = newName
|
|
||||||
withAnimation { keypairs[index] = keypairWithNewName }
|
|
||||||
saveKeypairs()
|
|
||||||
}
|
|
||||||
|
|
||||||
func deleteKey(_ keypair: Keypair) {
|
|
||||||
removeFromKeycahin(keypair: keypair)
|
|
||||||
let keyID = keypair.id
|
|
||||||
withAnimation { keypairs.removeAll(where: { $0.id == keyID }) }
|
|
||||||
saveKeypairs()
|
|
||||||
}
|
|
||||||
|
|
||||||
func importKey(type: KeyType, priv: String, name: String) {
|
|
||||||
if type == .ed25519 {
|
|
||||||
guard let importedKeypair = KeyManager.importSSHPrivkey(priv: priv) else { return }
|
|
||||||
saveToKeychain(importedKeypair)
|
|
||||||
saveKeypairs()
|
|
||||||
} else { fatalError() }
|
|
||||||
}
|
|
||||||
|
|
||||||
//MARK: generate keys
|
|
||||||
func generateKey(type: KeyType, comment: String) {
|
|
||||||
switch type {
|
|
||||||
case .ed25519:
|
|
||||||
let keypair = Keypair(
|
|
||||||
type: .ed25519,
|
|
||||||
name: comment,
|
|
||||||
privateKey: Curve25519.Signing.PrivateKey().rawRepresentation
|
|
||||||
)
|
|
||||||
saveToKeychain(keypair)
|
|
||||||
saveKeypairs()
|
|
||||||
}
|
|
||||||
loadKeypairs()
|
|
||||||
}
|
|
||||||
|
|
||||||
static func importSSHPubkey(pub: String) -> Data {
|
static func importSSHPubkey(pub: String) -> Data {
|
||||||
let split = pub.split(separator: " ")
|
let split = pub.split(separator: " ")
|
||||||
guard split.count == 3 else { return Data() }
|
guard split.count == 3 else { return Data() }
|
||||||
@@ -150,15 +151,14 @@ class KeyManager: ObservableObject {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static func makeSSHPubkey(_ keypair: Keypair) -> Data {
|
static func makeSSHPubkey(_ keypair: Keypair) -> Data {
|
||||||
let header = "ssh-ed25519"
|
|
||||||
var keyBlob: Data = Data()
|
var keyBlob: Data = Data()
|
||||||
//key type bit
|
//key type bit
|
||||||
keyBlob += encode(str: header)
|
keyBlob += encode(str: keypair.type.header)
|
||||||
//base64 blob bit
|
//base64 blob bit
|
||||||
keyBlob += encode(data: keypair.publicKey)
|
keyBlob += encode(data: keypair.publicKey)
|
||||||
|
|
||||||
let b64key = keyBlob.base64EncodedString()
|
let b64key = keyBlob.base64EncodedString()
|
||||||
let pubkeyline = "\(header) \(b64key) \(keypair.name)\n"
|
let pubkeyline = "\(keypair.type.header) \(b64key) \(keypair.name)\n"
|
||||||
return Data(pubkeyline.utf8)
|
return Data(pubkeyline.utf8)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -254,6 +254,7 @@ class KeyManager: ObservableObject {
|
|||||||
return content
|
return content
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//MARK: openssh conversion helpers
|
||||||
static func encode(str: String) -> Data {
|
static func encode(str: String) -> Data {
|
||||||
guard let utf8 = str.data(using: .utf8) else {
|
guard let utf8 = str.data(using: .utf8) else {
|
||||||
return Data()
|
return Data()
|
||||||
|
|||||||
@@ -16,4 +16,11 @@ enum KeyType: Codable, Equatable, Hashable, CustomStringConvertible, CaseIterabl
|
|||||||
return "Ed25519"
|
return "Ed25519"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var header: String {
|
||||||
|
switch self {
|
||||||
|
case .ed25519:
|
||||||
|
"ssh-ed25519"
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -25,10 +25,9 @@ struct Keypair: KeypairProtocol {
|
|||||||
var type: KeyType = .ed25519
|
var type: KeyType = .ed25519
|
||||||
var name: String = ""
|
var name: String = ""
|
||||||
var publicKey: Data {
|
var publicKey: Data {
|
||||||
if privateKey.isEmpty {
|
guard !privateKey.isEmpty else { return Data() }
|
||||||
print("not a valid ed25519 key")
|
switch type {
|
||||||
fatalError()
|
case .ed25519:
|
||||||
} else {
|
|
||||||
return (try? Curve25519.Signing.PrivateKey(rawRepresentation: privateKey).publicKey.rawRepresentation) ?? Data()
|
return (try? Curve25519.Signing.PrivateKey(rawRepresentation: privateKey).publicKey.rawRepresentation) ?? Data()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -82,9 +82,10 @@ struct ConnectionView: View {
|
|||||||
Picker("Private key", selection: $handler.host.privateKeyID) {
|
Picker("Private key", selection: $handler.host.privateKeyID) {
|
||||||
Text("None")
|
Text("None")
|
||||||
.tag(nil as UUID?)
|
.tag(nil as UUID?)
|
||||||
|
Divider()
|
||||||
ForEach(keyManager.keypairs) { keypair in
|
ForEach(keyManager.keypairs) { keypair in
|
||||||
Text(keypair.label)
|
Text(keypair.label)
|
||||||
.tag(keypair.id)
|
.tag(keypair.id as UUID?)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -90,14 +90,14 @@ struct KeyDetailView: View {
|
|||||||
VStack(alignment: .leading) {
|
VStack(alignment: .leading) {
|
||||||
Text("Public key")
|
Text("Public key")
|
||||||
.bold()
|
.bold()
|
||||||
Text(keypair.openSshPubkey.dropLast(2))
|
Text(keypair.openSshPubkey.trimmingCharacters(in: .whitespacesAndNewlines))
|
||||||
}
|
}
|
||||||
VStack(alignment: .leading) {
|
VStack(alignment: .leading) {
|
||||||
Text("Private key")
|
Text("Private key")
|
||||||
.bold()
|
.bold()
|
||||||
.frame(maxWidth: .infinity)
|
.frame(maxWidth: .infinity)
|
||||||
ZStack(alignment: .center) {
|
ZStack(alignment: .center) {
|
||||||
Text(keypair.openSshPrivkey.dropLast(2))
|
Text(keypair.openSshPrivkey.trimmingCharacters(in: .whitespacesAndNewlines))
|
||||||
.blur(radius: reveal ? 0 : 5)
|
.blur(radius: reveal ? 0 : 5)
|
||||||
VStack {
|
VStack {
|
||||||
Image(systemName: "eye.slash.fill")
|
Image(systemName: "eye.slash.fill")
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ struct KeyImporterView: View {
|
|||||||
|
|
||||||
@Environment(\.dismiss) var dismiss
|
@Environment(\.dismiss) var dismiss
|
||||||
|
|
||||||
@State var keyName: String = UIDevice().model + " " + Date().formatted()
|
@State var keyName: String = UIDevice().model + " at " + Date().formatted(date: .numeric, time: .omitted)
|
||||||
@State var privkeyStr: String = ""
|
@State var privkeyStr: String = ""
|
||||||
@State var keyType: KeyType = .ed25519
|
@State var keyType: KeyType = .ed25519
|
||||||
|
|
||||||
@@ -24,25 +24,33 @@ struct KeyImporterView: View {
|
|||||||
List {
|
List {
|
||||||
TextBox(label: "Name", text: $keyName, prompt: "A name for your key")
|
TextBox(label: "Name", text: $keyName, prompt: "A name for your key")
|
||||||
|
|
||||||
Picker("Key type", selection: $keyType) {
|
|
||||||
ForEach(KeyType.allCases, id: \.self) { type in
|
|
||||||
Text(type.description)
|
|
||||||
.tag(type)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.pickerStyle(SegmentedPickerStyle())
|
|
||||||
|
|
||||||
HStack {
|
HStack {
|
||||||
Text("Private Key")
|
Text("Key Type")
|
||||||
Spacer()
|
Picker("Key type", selection: $keyType) {
|
||||||
Text("Required")
|
ForEach(KeyType.allCases, id: \.self) { type in
|
||||||
.foregroundStyle(.red)
|
Text(type.description)
|
||||||
|
.tag(type)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.pickerStyle(SegmentedPickerStyle())
|
||||||
}
|
}
|
||||||
|
|
||||||
TextEditor(text: $privkeyStr)
|
Section {
|
||||||
|
HStack {
|
||||||
|
Text("Private Key")
|
||||||
|
Spacer()
|
||||||
|
Text("Required")
|
||||||
|
.foregroundStyle(.red)
|
||||||
|
}
|
||||||
|
.listRowSeparator(.hidden)
|
||||||
|
|
||||||
|
TextEditor(text: $privkeyStr)
|
||||||
|
.background(.black)
|
||||||
|
.clipShape(RoundedRectangle(cornerRadius: 10))
|
||||||
|
}
|
||||||
|
|
||||||
if !keypair.openSshPubkey.isEmpty {
|
if !keypair.openSshPubkey.isEmpty {
|
||||||
TextEditor(text: .constant(keypair.openSshPubkey))
|
Text(keypair.openSshPubkey.trimmingCharacters(in: .whitespacesAndNewlines))
|
||||||
.foregroundStyle(.gray)
|
.foregroundStyle(.gray)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -53,11 +61,14 @@ struct KeyImporterView: View {
|
|||||||
dismiss()
|
dismiss()
|
||||||
} label: {
|
} label: {
|
||||||
Text("Import")
|
Text("Import")
|
||||||
|
.font(.title)
|
||||||
|
.bold()
|
||||||
}
|
}
|
||||||
.onTapGesture {
|
.onTapGesture {
|
||||||
UINotificationFeedbackGenerator().notificationOccurred(.success)
|
UINotificationFeedbackGenerator().notificationOccurred(.success)
|
||||||
}
|
}
|
||||||
.buttonStyle(.borderedProminent)
|
.buttonStyle(.borderedProminent)
|
||||||
|
.padding()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -52,7 +52,7 @@ struct KeyManagerView: View {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Button("Generate a new Ed25519 Key") {
|
Button("Generate a new Ed25519 Key") {
|
||||||
let comment = UIDevice().model + " " + Date().formatted()
|
let comment = UIDevice().model + " at " + Date().formatted(date: .numeric, time: .omitted)
|
||||||
keyManager.generateKey(type: .ed25519, comment: comment)
|
keyManager.generateKey(type: .ed25519, comment: comment)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user