AuthView.swift (4761B)
1 // 2 // AuthView.swift 3 // lightninglink 4 // 5 // Created by William Casarin on 2022-08-08. 6 // 7 8 import SwiftUI 9 import CryptoKit 10 import CommonCrypto 11 12 13 struct AuthView: View { 14 let auth: LNUrlAuth 15 let lnlink: LNLink 16 @State var error: String? = nil 17 18 @Environment(\.dismiss) var dismiss 19 20 var body: some View { 21 VStack(spacing: 20) { 22 Text(auth.url.host ?? "Unknown") 23 .font(.largeTitle) 24 25 Button(action: { login() }) { 26 Text(auth.tag.capitalized) 27 .padding() 28 .font(.largeTitle) 29 } 30 .background { 31 Color.accentColor 32 } 33 .foregroundColor(Color.white) 34 .cornerRadius(20) 35 36 if let error = error { 37 Text(error) 38 .foregroundColor(Color.red) 39 .padding(20) 40 } 41 } 42 } 43 44 func login() { 45 let ln = LNSocket() 46 guard ln.connect_and_init(node_id: lnlink.node_id, host: lnlink.host) else { 47 error = "Could not connect to node" 48 return 49 } 50 guard let hex = make_secret_hex(self.auth) else { 51 error = "Could not make secret" 52 return 53 } 54 Task.init { 55 let res = rpc_makesecret(ln: ln, token: lnlink.token, hex: hex) 56 switch res { 57 case .failure(let err): 58 self.error = err.description 59 case .success(let makesec): 60 let sec = makesec.secret 61 await do_login(sec, auth: self.auth) 62 } 63 } 64 } 65 66 func do_login(_ hexsec: String, auth: LNUrlAuth) async { 67 var url_str = auth.url.absoluteString 68 69 var dersig = Array<UInt8>.init(repeating: 0, count: 72) 70 var pk = Array<UInt8>.init(repeating: 0, count: 33) 71 var sig = secp256k1_ecdsa_signature() 72 73 guard let sec = hex_decode(hexsec) else { 74 self.error = "Could not hex decode secret key" 75 return 76 } 77 78 guard let msg = hex_decode(auth.k1) else { 79 self.error = "Could not decode k1 challenge string as hex: '\(auth.k1)'" 80 return 81 } 82 83 84 let opts = UInt32(SECP256K1_CONTEXT_SIGN) 85 guard let ctx = secp256k1_context_create(opts) else { 86 self.error = "Could not create secp256k1 context" 87 return 88 } 89 90 var pubkey = secp256k1_pubkey() 91 92 //let msg2 = sha256(msg) 93 94 guard secp256k1_ecdsa_sign(ctx, &sig, msg, sec, nil, nil) == 1 else { 95 self.error = "Failed to sign" 96 return 97 } 98 99 var siglen: Int = 72 100 guard secp256k1_ecdsa_signature_serialize_der(ctx, &dersig, &siglen, &sig) == 1 else { 101 self.error = "Failed to encode DER ecdsa signature" 102 return 103 } 104 105 dersig = Array(dersig[..<siglen]) 106 107 defer { secp256k1_context_destroy(ctx) } 108 109 guard secp256k1_ec_pubkey_create(ctx, &pubkey, sec) == 1 else { 110 self.error = "Failed to get pubkey from keypair" 111 return 112 } 113 114 var pklen: Int = 33 115 guard secp256k1_ec_pubkey_serialize(ctx, &pk, &pklen, &pubkey, UInt32(SECP256K1_EC_COMPRESSED)) == 1 else { 116 self.error = "Failed to serialize pubkey" 117 return 118 } 119 120 let hex_key = hex_encode(pk) 121 let hex_sig = hex_encode(dersig) 122 123 url_str += "&sig=" + hex_sig + "&key=" + hex_key 124 125 guard let url = URL(string: url_str) else { 126 self.error = "Invalid url: \(url_str)" 127 return 128 } 129 130 // (data, resp) 131 guard let (data, resp) = try? await URLSession.shared.data(from: url) else { 132 self.error = "Login failed" 133 return 134 } 135 136 print("\(resp)") 137 print("\(data)") 138 139 dismiss() 140 } 141 142 } 143 144 func make_secret_hex(_ auth: LNUrlAuth) -> String? { 145 guard let host_data = auth.host.data(using: .utf8) else { 146 return nil 147 } 148 return hex_encode(Array(host_data)) 149 } 150 151 struct AuthView_Previews: PreviewProvider { 152 153 static var previews: some View { 154 let auth = LNUrlAuth(k1: "k1", tag: "login", url: URL(string: "jb55.com")!, host: "jb55.com") 155 let lnlink = LNLink(token: "", host: "", node_id: "") 156 AuthView(auth: auth, lnlink: lnlink) 157 } 158 } 159 160 func sha256(_ data: [UInt8]) -> [UInt8] { 161 var hash = [UInt8](repeating: 0, count: Int(CC_SHA256_DIGEST_LENGTH)) 162 var data = data 163 CC_SHA256(&data, CC_LONG(data.count), &hash) 164 return hash 165 }