lnlink

iOS app for connecting to lightning nodes
git clone git://jb55.com/lnlink
Log | Files | Refs | Submodules | README | LICENSE

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 }