NCKeychain.swift 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613
  1. //
  2. // NCKeychain.swift
  3. // Nextcloud
  4. //
  5. // Created by Marino Faggiana on 23/10/23.
  6. // Copyright © 2023 Marino Faggiana. All rights reserved.
  7. //
  8. // Author Marino Faggiana <marino.faggiana@nextcloud.com>
  9. //
  10. // This program is free software: you can redistribute it and/or modify
  11. // it under the terms of the GNU General Public License as published by
  12. // the Free Software Foundation, either version 3 of the License, or
  13. // (at your option) any later version.
  14. //
  15. // This program is distributed in the hope that it will be useful,
  16. // but WITHOUT ANY WARRANTY; without even the implied warranty of
  17. // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  18. // GNU General Public License for more details.
  19. //
  20. // You should have received a copy of the GNU General Public License
  21. // along with this program. If not, see <http://www.gnu.org/licenses/>.
  22. //
  23. import Foundation
  24. import UIKit
  25. import KeychainAccess
  26. @objc class NCKeychain: NSObject {
  27. let keychain = Keychain(service: "com.nextcloud.keychain")
  28. var showDescription: Bool {
  29. get {
  30. if let value = try? keychain.get("showDescription"), let result = Bool(value) {
  31. return result
  32. }
  33. return true
  34. }
  35. set {
  36. keychain["showDescription"] = String(newValue)
  37. }
  38. }
  39. var typeFilterScanDocument: NCGlobal.TypeFilterScanDocument {
  40. get {
  41. if let rawValue = try? keychain.get("ScanDocumentTypeFilter"), let value = NCGlobal.TypeFilterScanDocument(rawValue: rawValue) {
  42. return value
  43. } else {
  44. return .original
  45. }
  46. }
  47. set {
  48. keychain["ScanDocumentTypeFilter"] = newValue.rawValue
  49. }
  50. }
  51. @objc var passcode: String? {
  52. get {
  53. migrate(key: "passcodeBlock")
  54. if let value = try? keychain.get("passcodeBlock"), !value.isEmpty {
  55. return value
  56. }
  57. return nil
  58. }
  59. set {
  60. keychain["passcodeBlock"] = newValue
  61. }
  62. }
  63. var resetAppCounterFail: Bool {
  64. get {
  65. if let value = try? keychain.get("resetAppCounterFail"), let result = Bool(value) {
  66. return result
  67. }
  68. return false
  69. }
  70. set {
  71. keychain["resetAppCounterFail"] = String(newValue)
  72. }
  73. }
  74. var passcodeCounterFail: Int {
  75. get {
  76. if let value = try? keychain.get("passcodeCounterFail"), let result = Int(value) {
  77. return result
  78. }
  79. return 0
  80. }
  81. set {
  82. keychain["passcodeCounterFail"] = String(newValue)
  83. }
  84. }
  85. var passcodeCounterFailReset: Int {
  86. get {
  87. if let value = try? keychain.get("passcodeCounterFailReset"), let result = Int(value) {
  88. return result
  89. }
  90. return 0
  91. }
  92. set {
  93. keychain["passcodeCounterFailReset"] = String(newValue)
  94. }
  95. }
  96. var requestPasscodeAtStart: Bool {
  97. get {
  98. let keychainOLD = Keychain(service: "Crypto Cloud")
  99. if let value = keychainOLD["notPasscodeAtStart"], !value.isEmpty {
  100. if value == "true" {
  101. keychain["requestPasscodeAtStart"] = "false"
  102. } else if value == "false" {
  103. keychain["requestPasscodeAtStart"] = "true"
  104. }
  105. keychainOLD["notPasscodeAtStart"] = nil
  106. }
  107. if NCBrandOptions.shared.doNotAskPasscodeAtStartup {
  108. return false
  109. } else if let value = try? keychain.get("requestPasscodeAtStart"), let result = Bool(value) {
  110. return result
  111. }
  112. return true
  113. }
  114. set {
  115. keychain["requestPasscodeAtStart"] = String(newValue)
  116. }
  117. }
  118. var touchFaceID: Bool {
  119. get {
  120. migrate(key: "enableTouchFaceID")
  121. if let value = try? keychain.get("enableTouchFaceID"), let result = Bool(value) {
  122. return result
  123. }
  124. return false
  125. }
  126. set {
  127. keychain["enableTouchFaceID"] = String(newValue)
  128. }
  129. }
  130. var presentPasscode: Bool {
  131. return passcode != nil && requestPasscodeAtStart
  132. }
  133. var incrementalNumber: String {
  134. migrate(key: "incrementalnumber")
  135. var incrementalString = String(format: "%04ld", 0)
  136. if let value = try? keychain.get("incrementalnumber"), var result = Int(value) {
  137. result += 1
  138. incrementalString = String(format: "%04ld", result)
  139. }
  140. keychain["incrementalnumber"] = incrementalString
  141. return incrementalString
  142. }
  143. var showHiddenFiles: Bool {
  144. get {
  145. migrate(key: "showHiddenFiles")
  146. if let value = try? keychain.get("showHiddenFiles"), let result = Bool(value) {
  147. return result
  148. }
  149. return false
  150. }
  151. set {
  152. keychain["showHiddenFiles"] = String(newValue)
  153. }
  154. }
  155. var formatCompatibility: Bool {
  156. get {
  157. migrate(key: "formatCompatibility")
  158. if let value = try? keychain.get("formatCompatibility"), let result = Bool(value) {
  159. return result
  160. }
  161. return true
  162. }
  163. set {
  164. keychain["formatCompatibility"] = String(newValue)
  165. }
  166. }
  167. var disableFilesApp: Bool {
  168. get {
  169. migrate(key: "disablefilesapp")
  170. if let value = try? keychain.get("disablefilesapp"), let result = Bool(value) {
  171. return result
  172. }
  173. return false
  174. }
  175. set {
  176. keychain["disablefilesapp"] = String(newValue)
  177. }
  178. }
  179. var livePhoto: Bool {
  180. get {
  181. migrate(key: "livePhoto")
  182. if let value = try? keychain.get("livePhoto"), let result = Bool(value) {
  183. return result
  184. }
  185. return true
  186. }
  187. set {
  188. keychain["livePhoto"] = String(newValue)
  189. }
  190. }
  191. var disableCrashservice: Bool {
  192. get {
  193. migrate(key: "crashservice")
  194. if let value = try? keychain.get("crashservice"), let result = Bool(value) {
  195. return result
  196. }
  197. return false
  198. }
  199. set {
  200. keychain["crashservice"] = String(newValue)
  201. }
  202. }
  203. var logLevel: Int {
  204. get {
  205. migrate(key: "logLevel")
  206. if let value = try? keychain.get("logLevel"), let result = Int(value) {
  207. return result
  208. }
  209. return 1
  210. }
  211. set {
  212. keychain["logLevel"] = String(newValue)
  213. }
  214. }
  215. var accountRequest: Bool {
  216. get {
  217. migrate(key: "accountRequest")
  218. if let value = try? keychain.get("accountRequest"), let result = Bool(value) {
  219. return result
  220. }
  221. return false
  222. }
  223. set {
  224. keychain["accountRequest"] = String(newValue)
  225. }
  226. }
  227. var removePhotoCameraRoll: Bool {
  228. get {
  229. migrate(key: "removePhotoCameraRoll")
  230. if let value = try? keychain.get("removePhotoCameraRoll"), let result = Bool(value) {
  231. return result
  232. }
  233. return false
  234. }
  235. set {
  236. keychain["removePhotoCameraRoll"] = String(newValue)
  237. }
  238. }
  239. var privacyScreenEnabled: Bool {
  240. get {
  241. migrate(key: "privacyScreen")
  242. if let value = try? keychain.get("privacyScreen"), let result = Bool(value) {
  243. return result
  244. }
  245. return false
  246. }
  247. set {
  248. keychain["privacyScreen"] = String(newValue)
  249. }
  250. }
  251. var cleanUpDay: Int {
  252. get {
  253. migrate(key: "cleanUpDay")
  254. if let value = try? keychain.get("cleanUpDay"), let result = Int(value) {
  255. return result
  256. }
  257. return NCBrandOptions.shared.cleanUpDay
  258. }
  259. set {
  260. keychain["cleanUpDay"] = String(newValue)
  261. }
  262. }
  263. var textRecognitionStatus: Bool {
  264. get {
  265. migrate(key: "textRecognitionStatus")
  266. if let value = try? keychain.get("textRecognitionStatus"), let result = Bool(value) {
  267. return result
  268. }
  269. return false
  270. }
  271. set {
  272. keychain["textRecognitionStatus"] = String(newValue)
  273. }
  274. }
  275. var deleteAllScanImages: Bool {
  276. get {
  277. migrate(key: "deleteAllScanImages")
  278. if let value = try? keychain.get("deleteAllScanImages"), let result = Bool(value) {
  279. return result
  280. }
  281. return false
  282. }
  283. set {
  284. keychain["deleteAllScanImages"] = String(newValue)
  285. }
  286. }
  287. var qualityScanDocument: Double {
  288. get {
  289. migrate(key: "qualityScanDocument")
  290. if let value = try? keychain.get("qualityScanDocument"), let result = Double(value) {
  291. return result
  292. }
  293. return 2
  294. }
  295. set {
  296. keychain["qualityScanDocument"] = String(newValue)
  297. }
  298. }
  299. var appearanceAutomatic: Bool {
  300. get {
  301. if let value = try? keychain.get("appearanceAutomatic"), let result = Bool(value) {
  302. return result
  303. }
  304. return true
  305. }
  306. set {
  307. keychain["appearanceAutomatic"] = String(newValue)
  308. }
  309. }
  310. var appearanceInterfaceStyle: UIUserInterfaceStyle {
  311. get {
  312. if let value = try? keychain.get("appearanceInterfaceStyle") {
  313. if value == "light" {
  314. return .light
  315. } else {
  316. return .dark
  317. }
  318. }
  319. return .light
  320. }
  321. set {
  322. if newValue == .light {
  323. keychain["appearanceInterfaceStyle"] = "light"
  324. } else {
  325. keychain["appearanceInterfaceStyle"] = "dark"
  326. }
  327. }
  328. }
  329. var screenAwakeMode: AwakeMode {
  330. get {
  331. if let value = try? keychain.get("screenAwakeMode") {
  332. if value == "off" {
  333. return .off
  334. } else if value == "on" {
  335. return .on
  336. } else {
  337. return .whileCharging
  338. }
  339. }
  340. return .off
  341. }
  342. set {
  343. if newValue == .off {
  344. keychain["screenAwakeMode"] = "off"
  345. } else if newValue == .on {
  346. keychain["screenAwakeMode"] = "on"
  347. } else {
  348. keychain["screenAwakeMode"] = "whileCharging"
  349. }
  350. }
  351. }
  352. var fileNameType: Bool {
  353. get {
  354. if let value = try? keychain.get("fileNameType"), let result = Bool(value) {
  355. return result
  356. }
  357. return false
  358. }
  359. set {
  360. keychain["fileNameType"] = String(newValue)
  361. }
  362. }
  363. var fileNameOriginal: Bool {
  364. get {
  365. if let value = try? keychain.get("fileNameOriginal"), let result = Bool(value) {
  366. return result
  367. }
  368. return false
  369. }
  370. set {
  371. keychain["fileNameOriginal"] = String(newValue)
  372. }
  373. }
  374. var fileNameMask: String {
  375. get {
  376. if let value = try? keychain.get("fileNameMask") {
  377. return value
  378. }
  379. return ""
  380. }
  381. set {
  382. keychain["fileNameMask"] = String(newValue)
  383. }
  384. }
  385. // MARK: -
  386. @objc func getPassword(account: String) -> String {
  387. let key = "password" + account
  388. migrate(key: key)
  389. let password = (try? keychain.get(key)) ?? ""
  390. return password
  391. }
  392. func setPassword(account: String, password: String?) {
  393. let key = "password" + account
  394. keychain[key] = password
  395. }
  396. func setPersonalFilesOnly(account: String, value: Bool) {
  397. let key = "personalFilesOnly" + account
  398. keychain[key] = String(value)
  399. }
  400. func getPersonalFilesOnly(account: String) -> Bool {
  401. let key = "personalFilesOnly" + account
  402. if let value = try? keychain.get(key), let result = Bool(value) {
  403. return result
  404. } else {
  405. return false
  406. }
  407. }
  408. // MARK: - E2EE
  409. func getEndToEndCertificate(account: String) -> String? {
  410. let key = "EndToEndCertificate_" + account
  411. migrate(key: key)
  412. return try? keychain.get(key)
  413. }
  414. func setEndToEndCertificate(account: String, certificate: String?) {
  415. let key = "EndToEndCertificate_" + account
  416. keychain[key] = certificate
  417. }
  418. func getEndToEndPrivateKey(account: String) -> String? {
  419. let key = "EndToEndPrivateKey_" + account
  420. migrate(key: key)
  421. return try? keychain.get(key)
  422. }
  423. func setEndToEndPrivateKey(account: String, privateKey: String?) {
  424. let key = "EndToEndPrivateKey_" + account
  425. keychain[key] = privateKey
  426. }
  427. func getEndToEndPublicKey(account: String) -> String? {
  428. let key = "EndToEndPublicKeyServer_" + account
  429. migrate(key: key)
  430. return try? keychain.get(key)
  431. }
  432. func setEndToEndPublicKey(account: String, publicKey: String?) {
  433. let key = "EndToEndPublicKeyServer_" + account
  434. keychain[key] = publicKey
  435. }
  436. func getEndToEndPassphrase(account: String) -> String? {
  437. let key = "EndToEndPassphrase_" + account
  438. migrate(key: key)
  439. return try? keychain.get(key)
  440. }
  441. func setEndToEndPassphrase(account: String, passphrase: String?) {
  442. let key = "EndToEndPassphrase_" + account
  443. keychain[key] = passphrase
  444. }
  445. func isEndToEndEnabled(account: String) -> Bool {
  446. let capabilities = NCCapabilities.shared.getCapabilities(account: account)
  447. guard let certificate = getEndToEndCertificate(account: account), !certificate.isEmpty,
  448. let publicKey = getEndToEndPublicKey(account: account), !publicKey.isEmpty,
  449. let privateKey = getEndToEndPrivateKey(account: account), !privateKey.isEmpty,
  450. let passphrase = getEndToEndPassphrase(account: account), !passphrase.isEmpty,
  451. NCGlobal.shared.e2eeVersions.contains(capabilities.capabilityE2EEApiVersion) else { return false }
  452. return true
  453. }
  454. func clearAllKeysEndToEnd(account: String) {
  455. setEndToEndCertificate(account: account, certificate: nil)
  456. setEndToEndPrivateKey(account: account, privateKey: nil)
  457. setEndToEndPublicKey(account: account, publicKey: nil)
  458. setEndToEndPassphrase(account: account, passphrase: nil)
  459. }
  460. // MARK: - PUSHNOTIFICATION
  461. func getPushNotificationPublicKey(account: String) -> Data? {
  462. let key = "PNPublicKey" + account
  463. return try? keychain.getData(key)
  464. }
  465. @objc func setPushNotificationPublicKey(account: String, data: Data?) {
  466. let key = "PNPublicKey" + account
  467. keychain[data: key] = data
  468. }
  469. func getPushNotificationPrivateKey(account: String) -> Data? {
  470. let key = "PNPrivateKey" + account
  471. return try? keychain.getData(key)
  472. }
  473. @objc func setPushNotificationPrivateKey(account: String, data: Data?) {
  474. let key = "PNPrivateKey" + account
  475. keychain[data: key] = data
  476. }
  477. func getPushNotificationSubscribingPublicKey(account: String) -> String? {
  478. let key = "PNSubscribingPublicKey" + account
  479. return try? keychain.get(key)
  480. }
  481. func setPushNotificationSubscribingPublicKey(account: String, publicKey: String?) {
  482. let key = "PNSubscribingPublicKey" + account
  483. keychain[key] = publicKey
  484. }
  485. func getPushNotificationToken(account: String) -> String? {
  486. let key = "PNToken" + account
  487. return try? keychain.get(key)
  488. }
  489. func setPushNotificationToken(account: String, token: String?) {
  490. let key = "PNToken" + account
  491. keychain[key] = token
  492. }
  493. func getPushNotificationDeviceIdentifier(account: String) -> String? {
  494. let key = "PNDeviceIdentifier" + account
  495. return try? keychain.get(key)
  496. }
  497. func setPushNotificationDeviceIdentifier(account: String, deviceIdentifier: String?) {
  498. let key = "PNDeviceIdentifier" + account
  499. keychain[key] = deviceIdentifier
  500. }
  501. func getPushNotificationDeviceIdentifierSignature(account: String) -> String? {
  502. let key = "PNDeviceIdentifierSignature" + account
  503. return try? keychain.get(key)
  504. }
  505. func setPushNotificationDeviceIdentifierSignature(account: String, deviceIdentifierSignature: String?) {
  506. let key = "PNDeviceIdentifierSignature" + account
  507. keychain[key] = deviceIdentifierSignature
  508. }
  509. func clearAllKeysPushNotification(account: String) {
  510. setPushNotificationPublicKey(account: account, data: nil)
  511. setPushNotificationSubscribingPublicKey(account: account, publicKey: nil)
  512. setPushNotificationPrivateKey(account: account, data: nil)
  513. setPushNotificationToken(account: account, token: nil)
  514. setPushNotificationDeviceIdentifier(account: account, deviceIdentifier: nil)
  515. setPushNotificationDeviceIdentifierSignature(account: account, deviceIdentifierSignature: nil)
  516. }
  517. // MARK: - Certificates
  518. func setClientCertificate(account: String, p12Data: Data?, p12Password: String?) {
  519. var key = "ClientCertificateData" + account
  520. keychain[data: key] = p12Data
  521. key = "ClientCertificatePassword" + account
  522. keychain[key] = p12Password
  523. }
  524. func getClientCertificate(account: String) -> (p12Data: Data?, p12Password: String?) {
  525. var key = "ClientCertificateData" + account
  526. let data = try? keychain.getData(key)
  527. key = "ClientCertificatePassword" + account
  528. let password = keychain[key]
  529. return (data, password)
  530. }
  531. // MARK: -
  532. private func migrate(key: String) {
  533. let keychainOLD = Keychain(service: "Crypto Cloud")
  534. if let value = keychainOLD[key], !value.isEmpty {
  535. keychain[key] = value
  536. keychainOLD[key] = nil
  537. }
  538. }
  539. func removeAll() {
  540. try? keychain.removeAll()
  541. }
  542. }