Keys.swift (5539B)
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 init(pubkey: Pubkey, privkey: Privkey) { 26 self.pubkey = pubkey 27 self.privkey = privkey 28 } 29 30 init?(privkey: Privkey) { 31 self.privkey = privkey 32 guard let pubkey = privkey_to_pubkey_raw(sec: privkey.bytes) else { return nil } 33 self.pubkey = pubkey 34 } 35 36 func to_keypair() -> Keypair { 37 return Keypair(pubkey: pubkey, privkey: privkey) 38 } 39 } 40 41 struct Keypair { 42 let pubkey: Pubkey 43 let privkey: Privkey? 44 //let pubkey_bech32: String 45 //let privkey_bech32: String? 46 47 static var empty: Keypair { 48 Keypair(pubkey: .empty, privkey: nil) 49 } 50 51 func to_full() -> FullKeypair? { 52 guard let privkey = self.privkey else { 53 return nil 54 } 55 56 return FullKeypair(pubkey: pubkey, privkey: privkey) 57 } 58 59 static func just_pubkey(_ pk: Pubkey) -> Keypair { 60 return .init(pubkey: pk, privkey: nil) 61 } 62 63 init(pubkey: Pubkey, privkey: Privkey?) { 64 self.pubkey = pubkey 65 self.privkey = privkey 66 //self.pubkey_bech32 = pubkey.npub 67 //self.privkey_bech32 = privkey?.nsec 68 } 69 } 70 71 enum Bech32Key { 72 case pub(Pubkey) 73 case sec(Privkey) 74 } 75 76 func decode_bech32_key(_ key: String) -> Bech32Key? { 77 guard let decoded = try? bech32_decode(key), 78 decoded.data.count == 32 79 else { 80 return nil 81 } 82 83 if decoded.hrp == "npub" { 84 return .pub(Pubkey(decoded.data)) 85 } else if decoded.hrp == "nsec" { 86 return .sec(Privkey(decoded.data)) 87 } 88 89 return nil 90 } 91 92 func bech32_privkey(_ privkey: Privkey) -> String { 93 return bech32_encode(hrp: "nsec", privkey.bytes) 94 } 95 96 func bech32_pubkey(_ pubkey: Pubkey) -> String { 97 return bech32_encode(hrp: "npub", pubkey.bytes) 98 } 99 100 func bech32_pubkey_decode(_ pubkey: String) -> Pubkey? { 101 guard let decoded = try? bech32_decode(pubkey), 102 decoded.hrp == "npub", 103 decoded.data.count == 32 104 else { 105 return nil 106 } 107 108 return Pubkey(decoded.data) 109 } 110 111 func bech32_nopre_pubkey(_ pubkey: Pubkey) -> String { 112 return bech32_encode(hrp: "", pubkey.bytes) 113 } 114 115 func bech32_note_id(_ evid: NoteId) -> String { 116 return bech32_encode(hrp: "note", evid.bytes) 117 } 118 119 func generate_new_keypair() -> FullKeypair { 120 let key = try! secp256k1.Signing.PrivateKey() 121 let privkey = Privkey(key.rawRepresentation) 122 let pubkey = Pubkey(Data(key.publicKey.xonly.bytes)) 123 return FullKeypair(pubkey: pubkey, privkey: privkey) 124 } 125 126 func privkey_to_pubkey_raw(sec: [UInt8]) -> Pubkey? { 127 guard let key = try? secp256k1.Signing.PrivateKey(rawRepresentation: sec) else { 128 return nil 129 } 130 return Pubkey(Data(key.publicKey.xonly.bytes)) 131 } 132 133 func privkey_to_pubkey(privkey: Privkey) -> Pubkey? { 134 return privkey_to_pubkey_raw(sec: privkey.bytes) 135 } 136 137 func save_pubkey(pubkey: Pubkey) { 138 DamusUserDefaults.standard.set(pubkey.hex(), forKey: "pubkey") 139 } 140 141 enum Keys { 142 @KeychainStorage(account: "privkey") 143 static var privkey: String? 144 } 145 146 func save_privkey(privkey: Privkey) throws { 147 Keys.privkey = privkey.hex() 148 } 149 150 func clear_saved_privkey() throws { 151 Keys.privkey = nil 152 } 153 154 func clear_saved_pubkey() { 155 DamusUserDefaults.standard.removeObject(forKey: "pubkey") 156 } 157 158 func save_keypair(pubkey: Pubkey, privkey: Privkey) throws { 159 save_pubkey(pubkey: pubkey) 160 try save_privkey(privkey: privkey) 161 } 162 163 func clear_keypair() throws { 164 try clear_saved_privkey() 165 clear_saved_pubkey() 166 } 167 168 func get_saved_keypair() -> Keypair? { 169 do { 170 try removePrivateKeyFromUserDefaults() 171 172 guard let pubkey = get_saved_pubkey(), 173 let pk = hex_decode(pubkey) 174 else { 175 return nil 176 } 177 178 let privkey = get_saved_privkey().flatMap { sec in 179 hex_decode(sec).map { Privkey(Data($0)) } 180 } 181 182 return Keypair(pubkey: Pubkey(Data(pk)), privkey: privkey) 183 } catch { 184 return nil 185 } 186 } 187 188 func get_saved_pubkey() -> String? { 189 return DamusUserDefaults.standard.string(forKey: "pubkey") 190 } 191 192 func get_saved_privkey() -> String? { 193 let mkey = Keys.privkey 194 return mkey.map { $0.trimmingCharacters(in: .whitespaces) } 195 } 196 197 /** 198 Detects whether a string might contain an nsec1 prefixed private key. 199 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. 200 */ 201 func contentContainsPrivateKey(_ content: String) -> Bool { 202 if #available(iOS 16.0, *) { 203 return content.contains(/nsec1[02-9ac-z]+/) 204 } else { 205 let regex = try! NSRegularExpression(pattern: "nsec1[02-9ac-z]+") 206 return (regex.firstMatch(in: content, range: NSRange(location: 0, length: content.count)) != nil) 207 } 208 209 } 210 211 fileprivate func removePrivateKeyFromUserDefaults() throws { 212 guard let privkey_str = DamusUserDefaults.standard.string(forKey: "privkey"), 213 let privkey = hex_decode_privkey(privkey_str) 214 else { return } 215 216 try save_privkey(privkey: privkey) 217 DamusUserDefaults.standard.removeObject(forKey: "privkey") 218 }