Keys.swift (5233B)
1 // 2 // Keys.swift 3 // damus 4 // 5 // Created by William Casarin on 2022-05-21. 6 // 7 8 import Foundation 9 import secp256k1 10 11 let PUBKEY_HRP = "npub" 12 13 // some random pubkey 14 let ANON_PUBKEY = Pubkey(Data([ 15 0x85, 0x41, 0x5d, 0x63, 0x5c, 0x2b, 0xaf, 0x55, 16 0xf5, 0xb9, 0xa1, 0xa6, 0xce, 0xb7, 0x75, 0xcc, 17 0x5c, 0x45, 0x4a, 0x3a, 0x61, 0xb5, 0x3f, 0xe8, 18 0x50, 0x42, 0xdc, 0x42, 0xac, 0xe1, 0x7f, 0x12 19 ])) 20 21 struct FullKeypair: Equatable { 22 let pubkey: Pubkey 23 let privkey: Privkey 24 25 func to_keypair() -> Keypair { 26 return Keypair(pubkey: pubkey, privkey: privkey) 27 } 28 } 29 30 struct Keypair { 31 let pubkey: Pubkey 32 let privkey: Privkey? 33 //let pubkey_bech32: String 34 //let privkey_bech32: String? 35 36 static var empty: Keypair { 37 Keypair(pubkey: .empty, privkey: nil) 38 } 39 40 func to_full() -> FullKeypair? { 41 guard let privkey = self.privkey else { 42 return nil 43 } 44 45 return FullKeypair(pubkey: pubkey, privkey: privkey) 46 } 47 48 static func just_pubkey(_ pk: Pubkey) -> Keypair { 49 return .init(pubkey: pk, privkey: nil) 50 } 51 52 init(pubkey: Pubkey, privkey: Privkey?) { 53 self.pubkey = pubkey 54 self.privkey = privkey 55 //self.pubkey_bech32 = pubkey.npub 56 //self.privkey_bech32 = privkey?.nsec 57 } 58 } 59 60 enum Bech32Key { 61 case pub(Pubkey) 62 case sec(Privkey) 63 } 64 65 func decode_bech32_key(_ key: String) -> Bech32Key? { 66 guard let decoded = try? bech32_decode(key), 67 decoded.data.count == 32 68 else { 69 return nil 70 } 71 72 if decoded.hrp == "npub" { 73 return .pub(Pubkey(decoded.data)) 74 } else if decoded.hrp == "nsec" { 75 return .sec(Privkey(decoded.data)) 76 } 77 78 return nil 79 } 80 81 func bech32_privkey(_ privkey: Privkey) -> String { 82 return bech32_encode(hrp: "nsec", privkey.bytes) 83 } 84 85 func bech32_pubkey(_ pubkey: Pubkey) -> String { 86 return bech32_encode(hrp: "npub", pubkey.bytes) 87 } 88 89 func bech32_pubkey_decode(_ pubkey: String) -> Pubkey? { 90 guard let decoded = try? bech32_decode(pubkey), 91 decoded.hrp == "npub", 92 decoded.data.count == 32 93 else { 94 return nil 95 } 96 97 return Pubkey(decoded.data) 98 } 99 100 func bech32_nopre_pubkey(_ pubkey: Pubkey) -> String { 101 return bech32_encode(hrp: "", pubkey.bytes) 102 } 103 104 func bech32_note_id(_ evid: NoteId) -> String { 105 return bech32_encode(hrp: "note", evid.bytes) 106 } 107 108 func generate_new_keypair() -> FullKeypair { 109 let key = try! secp256k1.Signing.PrivateKey() 110 let privkey = Privkey(key.rawRepresentation) 111 let pubkey = Pubkey(Data(key.publicKey.xonly.bytes)) 112 return FullKeypair(pubkey: pubkey, privkey: privkey) 113 } 114 115 func privkey_to_pubkey_raw(sec: [UInt8]) -> Pubkey? { 116 guard let key = try? secp256k1.Signing.PrivateKey(rawRepresentation: sec) else { 117 return nil 118 } 119 return Pubkey(Data(key.publicKey.xonly.bytes)) 120 } 121 122 func privkey_to_pubkey(privkey: Privkey) -> Pubkey? { 123 return privkey_to_pubkey_raw(sec: privkey.bytes) 124 } 125 126 func save_pubkey(pubkey: Pubkey) { 127 DamusUserDefaults.standard.set(pubkey.hex(), forKey: "pubkey") 128 } 129 130 enum Keys { 131 @KeychainStorage(account: "privkey") 132 static var privkey: String? 133 } 134 135 func save_privkey(privkey: Privkey) throws { 136 Keys.privkey = privkey.hex() 137 } 138 139 func clear_saved_privkey() throws { 140 Keys.privkey = nil 141 } 142 143 func clear_saved_pubkey() { 144 DamusUserDefaults.standard.removeObject(forKey: "pubkey") 145 } 146 147 func save_keypair(pubkey: Pubkey, privkey: Privkey) throws { 148 save_pubkey(pubkey: pubkey) 149 try save_privkey(privkey: privkey) 150 } 151 152 func clear_keypair() throws { 153 try clear_saved_privkey() 154 clear_saved_pubkey() 155 } 156 157 func get_saved_keypair() -> Keypair? { 158 do { 159 try removePrivateKeyFromUserDefaults() 160 161 guard let pubkey = get_saved_pubkey(), 162 let pk = hex_decode(pubkey) 163 else { 164 return nil 165 } 166 167 let privkey = get_saved_privkey().flatMap { sec in 168 hex_decode(sec).map { Privkey(Data($0)) } 169 } 170 171 return Keypair(pubkey: Pubkey(Data(pk)), privkey: privkey) 172 } catch { 173 return nil 174 } 175 } 176 177 func get_saved_pubkey() -> String? { 178 return DamusUserDefaults.standard.string(forKey: "pubkey") 179 } 180 181 func get_saved_privkey() -> String? { 182 let mkey = Keys.privkey 183 return mkey.map { $0.trimmingCharacters(in: .whitespaces) } 184 } 185 186 /** 187 Detects whether a string might contain an nsec1 prefixed private key. 188 It does not determine if it's the current user's private key and does not verify if it is properly encoded or has the right length. 189 */ 190 func contentContainsPrivateKey(_ content: String) -> Bool { 191 if #available(iOS 16.0, *) { 192 return content.contains(/nsec1[02-9ac-z]+/) 193 } else { 194 let regex = try! NSRegularExpression(pattern: "nsec1[02-9ac-z]+") 195 return (regex.firstMatch(in: content, range: NSRange(location: 0, length: content.count)) != nil) 196 } 197 198 } 199 200 fileprivate func removePrivateKeyFromUserDefaults() throws { 201 guard let privkey_str = DamusUserDefaults.standard.string(forKey: "privkey"), 202 let privkey = hex_decode_privkey(privkey_str) 203 else { return } 204 205 try save_privkey(privkey: privkey) 206 DamusUserDefaults.standard.removeObject(forKey: "privkey") 207 }