@capacitor-community/sqlite 5.0.5-2 → 5.0.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (44) hide show
  1. package/android/src/main/java/com/getcapacitor/community/database/sqlite/CapacitorSQLite.java +118 -156
  2. package/android/src/main/java/com/getcapacitor/community/database/sqlite/CapacitorSQLitePlugin.java +81 -249
  3. package/android/src/main/java/com/getcapacitor/community/database/sqlite/NotificationCenter.java +1 -1
  4. package/android/src/main/java/com/getcapacitor/community/database/sqlite/RetHandler.java +0 -12
  5. package/android/src/main/java/com/getcapacitor/community/database/sqlite/SQLite/Database.java +184 -40
  6. package/android/src/main/java/com/getcapacitor/community/database/sqlite/SQLite/ImportExportJson/ExportToJson.java +141 -135
  7. package/android/src/main/java/com/getcapacitor/community/database/sqlite/SQLite/ImportExportJson/ImportFromJson.java +2 -1
  8. package/android/src/main/java/com/getcapacitor/community/database/sqlite/SQLite/ImportExportJson/UtilsEncryption.java +111 -0
  9. package/android/src/main/java/com/getcapacitor/community/database/sqlite/SQLite/UtilsBiometric.java +0 -4
  10. package/android/src/main/java/com/getcapacitor/community/database/sqlite/SQLite/UtilsFile.java +30 -18
  11. package/android/src/main/java/com/getcapacitor/community/database/sqlite/SQLite/UtilsMigrate.java +12 -4
  12. package/android/src/main/java/com/getcapacitor/community/database/sqlite/SQLite/UtilsNCDatabase.java +4 -1
  13. package/android/src/main/java/com/getcapacitor/community/database/sqlite/SQLite/UtilsSQLCipher.java +8 -6
  14. package/android/src/main/java/com/getcapacitor/community/database/sqlite/SQLite/UtilsSQLite.java +14 -28
  15. package/android/src/main/java/com/getcapacitor/community/database/sqlite/SQLite/UtilsSecret.java +3 -4
  16. package/android/src/main/java/com/getcapacitor/community/database/sqlite/SQLite/UtilsUpgrade.java +0 -1
  17. package/dist/esm/definitions.d.ts +91 -4
  18. package/dist/esm/definitions.js +79 -19
  19. package/dist/esm/definitions.js.map +1 -1
  20. package/dist/esm/web.d.ts +3 -1
  21. package/dist/esm/web.js +8 -0
  22. package/dist/esm/web.js.map +1 -1
  23. package/dist/plugin.cjs.js +87 -19
  24. package/dist/plugin.cjs.js.map +1 -1
  25. package/dist/plugin.js +87 -19
  26. package/dist/plugin.js.map +1 -1
  27. package/electron/dist/plugin.js +333 -148
  28. package/electron/dist/plugin.js.map +1 -1
  29. package/electron/rollup.config.js +2 -0
  30. package/ios/Plugin/CapacitorSQLite.swift +125 -92
  31. package/ios/Plugin/CapacitorSQLitePlugin.swift +6 -3
  32. package/ios/Plugin/Database.swift +45 -19
  33. package/ios/Plugin/ImportExportJson/ExportToJson.swift +10 -5
  34. package/ios/Plugin/ImportExportJson/ImportData.swift +434 -0
  35. package/ios/Plugin/ImportExportJson/ImportFromJson.swift +47 -59
  36. package/ios/Plugin/ImportExportJson/JsonSQLite.swift +7 -0
  37. package/ios/Plugin/Utils/UtilsDownloadFromHTTP.swift +61 -61
  38. package/ios/Plugin/Utils/UtilsDrop.swift +2 -1
  39. package/ios/Plugin/Utils/UtilsJson.swift +123 -1
  40. package/ios/Plugin/Utils/UtilsSQLCipher.swift +254 -23
  41. package/ios/Plugin/Utils/UtilsUpgrade.swift +0 -1
  42. package/package.json +2 -2
  43. package/src/definitions.ts +171 -18
  44. package/src/web.ts +10 -0
@@ -34,25 +34,24 @@ class ImportFromJson {
34
34
 
35
35
  // MARK: - ImportFromJson - CreateDatabaseSchema
36
36
 
37
- class func createDatabaseSchema(mDB: Database,
38
- jsonSQLite: JsonSQLite)
37
+ class func createDatabaseSchema(mDB: Database, tables: [ImportTable],
38
+ mode: String, version: Int)
39
39
  throws -> Int {
40
40
  let msg = "importFromJson: "
41
41
  var changes: Int = -1
42
- let version: Int = jsonSQLite.version
43
42
 
44
43
  do {
45
44
  // Set PRAGMAS
46
45
  try UtilsSQLCipher.setVersion(mDB: mDB,
47
46
  version: version)
48
- if jsonSQLite.mode == "full" {
47
+ if mode == "full" {
49
48
  // Drop All Tables, Indexes and Triggers
50
49
  try _ = UtilsDrop.dropAll(mDB: mDB)
51
50
  }
52
51
  // create database schema
53
52
  changes = try ImportFromJson
54
53
  .createSchema(mDB: mDB,
55
- jsonSQLite: jsonSQLite)
54
+ tables: tables, mode: mode)
56
55
  let msg = "Schema creation completed changes: \(changes)"
57
56
  notifyImportProgressEvent(msg: msg)
58
57
  return changes
@@ -76,8 +75,8 @@ class ImportFromJson {
76
75
  // MARK: - ImportFromJson - createSchema
77
76
 
78
77
  // swiftlint:disable function_body_length
79
- class func createSchema(mDB: Database,
80
- jsonSQLite: JsonSQLite) throws -> Int {
78
+ class func createSchema(mDB: Database, tables: [ImportTable],
79
+ mode: String) throws -> Int {
81
80
  var changes: Int = 0
82
81
  var initChanges: Int = 0
83
82
  do {
@@ -88,7 +87,7 @@ class ImportFromJson {
88
87
  }
89
88
  // Create a Schema Statements
90
89
  let statements = ImportFromJson
91
- .createSchemaStatement(jsonSQLite: jsonSQLite )
90
+ .createSchemaStatement(tables: tables, mode: mode )
92
91
  if statements.count > 0 {
93
92
  let joined = statements.joined(separator: "\n")
94
93
  let mStmt: String = joined.replacingOccurrences(of: "\'", with: "'")
@@ -132,7 +131,7 @@ class ImportFromJson {
132
131
  .createSchema(message: message)
133
132
  }
134
133
  } else {
135
- if jsonSQLite.mode == "partial" {
134
+ if mode == "partial" {
136
135
  changes = 0
137
136
  // Commit the transaction
138
137
  try UtilsSQLCipher.commitTransaction(mDB: mDB)
@@ -144,16 +143,15 @@ class ImportFromJson {
144
143
 
145
144
  // MARK: - ImportFromJson - createSchemaStatement
146
145
 
147
- class func createSchemaStatement(jsonSQLite: JsonSQLite)
148
- -> [String] {
146
+ class func createSchemaStatement(tables: [ImportTable],
147
+ mode: String) -> [String] {
149
148
  // Create the Database Schema
150
149
  var statements: [String] = []
151
150
  // Loop through Tables
152
- for ipos in 0..<jsonSQLite.tables.count {
153
- let mode: String = jsonSQLite.mode
154
- let tableName: String = jsonSQLite.tables[ipos].name
155
- if let mSchema: [JsonColumn] =
156
- jsonSQLite.tables[ipos].schema {
151
+ for ipos in 0..<tables.count {
152
+ let mode: String = mode
153
+ let tableName: String = tables[ipos].name
154
+ if let mSchema: [ImportColumn] = tables[ipos].schema {
157
155
  if mSchema.count > 0 {
158
156
  let stmt: [String] =
159
157
  ImportFromJson.createTableSchema(
@@ -162,8 +160,7 @@ class ImportFromJson {
162
160
  statements.append(contentsOf: stmt)
163
161
  }
164
162
  }
165
- if let mIndexes: [JsonIndex] =
166
- jsonSQLite.tables[ipos].indexes {
163
+ if let mIndexes: [ImportIndex] = tables[ipos].indexes {
167
164
  if mIndexes.count > 0 {
168
165
  let stmt: [String] =
169
166
  ImportFromJson.createTableIndexes(
@@ -171,8 +168,7 @@ class ImportFromJson {
171
168
  statements.append(contentsOf: stmt)
172
169
  }
173
170
  }
174
- if let mTriggers: [JsonTrigger] =
175
- jsonSQLite.tables[ipos].triggers {
171
+ if let mTriggers: [ImportTrigger] = tables[ipos].triggers {
176
172
  if mTriggers.count > 0 {
177
173
  let stmt: [String] =
178
174
  ImportFromJson.createTableTriggers(
@@ -188,7 +184,7 @@ class ImportFromJson {
188
184
 
189
185
  // swiftlint:disable function_body_length
190
186
  // swiftlint:disable cyclomatic_complexity
191
- class func createTableSchema(mSchema: [JsonColumn],
187
+ class func createTableSchema(mSchema: [ImportColumn],
192
188
  tableName: String, mode: String)
193
189
  -> [String] {
194
190
  var statements: [String] = []
@@ -251,7 +247,7 @@ class ImportFromJson {
251
247
 
252
248
  // MARK: - ImportFromJson - CreateTableIndexes
253
249
 
254
- class func createTableIndexes(mIndexes: [JsonIndex],
250
+ class func createTableIndexes(mIndexes: [ImportIndex],
255
251
  tableName: String) -> [String] {
256
252
  var statements: [String] = []
257
253
  for jpos in 0..<mIndexes.count {
@@ -279,7 +275,7 @@ class ImportFromJson {
279
275
 
280
276
  // MARK: - ImportFromJson - CreateTableTriggers
281
277
 
282
- class func createTableTriggers(mTriggers: [JsonTrigger],
278
+ class func createTableTriggers(mTriggers: [ImportTrigger],
283
279
  tableName: String) -> [String] {
284
280
  var statements: [String] = []
285
281
 
@@ -308,8 +304,8 @@ class ImportFromJson {
308
304
 
309
305
  // swiftlint:disable function_body_length
310
306
  class func createDatabaseData(mDB: Database,
311
- jsonSQLite: JsonSQLite)
312
- throws -> Int {
307
+ tables: [ImportTable],
308
+ mode: String) throws -> Int {
313
309
  var changes: Int = -1
314
310
  var initChanges: Int = -1
315
311
  var isValue: Bool = false
@@ -322,19 +318,19 @@ class ImportFromJson {
322
318
  throw ImportFromJsonError.createDatabaseData(message: message)
323
319
  }
324
320
  // Loop on tables to create Data
325
- for ipos in 0..<jsonSQLite.tables.count {
326
- if let mValues = jsonSQLite.tables[ipos].values {
321
+ for ipos in 0..<tables.count {
322
+ if let mValues = tables[ipos].values {
327
323
  if mValues.count > 0 {
328
324
  isValue = true
329
325
  do {
330
- let tableName = jsonSQLite.tables[ipos].name
326
+ let tableName = tables[ipos].name
331
327
  try ImportFromJson.createTableData(
332
328
  mDB: mDB,
333
- mode: jsonSQLite.mode,
329
+ mode: mode,
334
330
  mValues: mValues,
335
331
  tableName: tableName)
336
332
  let msg = "Table \(tableName) data creation completed " +
337
- "\(ipos + 1)/\(jsonSQLite.tables.count) ..."
333
+ "\(ipos + 1)/\(tables.count) ..."
338
334
  notifyImportProgressEvent(msg: msg)
339
335
 
340
336
  } catch ImportFromJsonError
@@ -382,8 +378,9 @@ class ImportFromJson {
382
378
  // swiftlint:disable function_body_length
383
379
  class func createTableData(
384
380
  mDB: Database, mode: String,
385
- mValues: [[UncertainValue<String, Int, Double>]],
381
+ mValues: [[Any]],
386
382
  tableName: String) throws {
383
+ var lastId: Int64 = -1
387
384
  // Check if table exists
388
385
  do {
389
386
  let isTab: Bool = try UtilsJson
@@ -409,8 +406,7 @@ class ImportFromJson {
409
406
  }
410
407
  for jpos in 0..<mValues.count {
411
408
  // Check row validity
412
- let row: [UncertainValue<String, Int, Double>] =
413
- mValues[jpos]
409
+ var rowValues = mValues[jpos]
414
410
  var isRun: Bool = true
415
411
 
416
412
  /* Remove types checking for allowing RDBMS Types
@@ -429,10 +425,9 @@ class ImportFromJson {
429
425
  "tableName": tableName]
430
426
  let stmt: String = try ImportFromJson
431
427
  .createRowStatement(mDB: mDB, data: data,
432
- row: row,
428
+ row: rowValues,
433
429
  jsonNamesTypes: jsonNamesTypes)
434
- var rowValues = UtilsJson.getValuesFromRow(
435
- rowValues: row)
430
+
436
431
  isRun = try UtilsJson.checkUpdate(mDB: mDB, stmt: stmt,
437
432
  values: rowValues,
438
433
  tableName: tableName,
@@ -443,8 +438,9 @@ class ImportFromJson {
443
438
  if stmt.prefix(6) == "DELETE" {
444
439
  rowValues = []
445
440
  }
446
- let lastId: Int64 = try UtilsSQLCipher.prepareSQL(
447
- mDB: mDB, sql: stmt, values: rowValues, fromJson: true)
441
+ let resp = try UtilsSQLCipher.prepareSQL(
442
+ mDB: mDB, sql: stmt, values: rowValues, fromJson: true, returnMode: "no")
443
+ lastId = resp.0
448
444
  if lastId < 0 {
449
445
  throw ImportFromJsonError.createTableData(
450
446
  message: "lastId < 0")
@@ -469,7 +465,7 @@ class ImportFromJson {
469
465
  class func createRowStatement(
470
466
  mDB: Database,
471
467
  data: [String: Any],
472
- row: [UncertainValue<String, Int, Double>],
468
+ row: [Any],
473
469
  jsonNamesTypes: JsonNamesTypes) throws -> String {
474
470
  var stmt: String = ""
475
471
  var retisIdExists: Bool = false
@@ -487,19 +483,11 @@ class ImportFromJson {
487
483
  message: message + " tableName")
488
484
  }
489
485
  do {
490
- if let rwValue: Any = row[0].value {
491
- retisIdExists = try UtilsJson.isIdExist(
492
- mDB: mDB, tableName: tableName,
493
- firstColumnName: jsonNamesTypes.names[0],
494
- key: rwValue)
495
- } else {
496
- var message: String = "createRowStatement: Table "
497
- message.append("\(tableName) values row[0] does not ")
498
- message.append("exist")
499
- throw ImportFromJsonError.createRowStatement(
500
- message: message)
501
- }
502
-
486
+ let rwValue = row[0]
487
+ retisIdExists = try UtilsJson.isIdExist(
488
+ mDB: mDB, tableName: tableName,
489
+ firstColumnName: jsonNamesTypes.names[0],
490
+ key: rwValue)
503
491
  } catch UtilsJsonError.isIdExists(let message) {
504
492
  throw ImportFromJsonError.createRowStatement(
505
493
  message: message)
@@ -518,13 +506,13 @@ class ImportFromJson {
518
506
  let idxDelete: Int = jsonNamesTypes
519
507
  .names.firstIndex(where: {$0 == "sql_deleted"}) ?? -1
520
508
  if idxDelete >= 0 {
521
- if let delValue = row[idxDelete].value as? Int {
509
+ if let delValue = row[idxDelete] as? Int {
522
510
  if delValue == 1 {
523
511
  isUpdate = false
524
512
  stmt = "DELETE FROM \(tableName) WHERE "
525
- if let rwValue = row[0].value as? String {
513
+ if let rwValue = row[0] as? String {
526
514
  stmt += "\(jsonNamesTypes.names[0]) = '\(rwValue)';"
527
- } else if let rwValue = row[0].value as? Int {
515
+ } else if let rwValue = row[0] as? Int {
528
516
  stmt += "\(jsonNamesTypes.names[0]) = \(rwValue);"
529
517
  } else {
530
518
  var msg: String = "importFromJson: Table "
@@ -548,9 +536,9 @@ class ImportFromJson {
548
536
  }
549
537
 
550
538
  stmt = "UPDATE \(tableName) SET \(setString) WHERE "
551
- if let rwValue = row[0].value as? String {
539
+ if let rwValue = row[0] as? String {
552
540
  stmt += "\(jsonNamesTypes.names[0]) = '\(rwValue)';"
553
- } else if let rwValue = row[0].value as? Int {
541
+ } else if let rwValue = row[0] as? Int {
554
542
  stmt += "\(jsonNamesTypes.names[0]) = \(rwValue);"
555
543
  } else {
556
544
  var msg: String = "importFromJson: Table "
@@ -568,7 +556,7 @@ class ImportFromJson {
568
556
  // MARK: - ImportFromJson - createViews
569
557
 
570
558
  // swiftlint:disable function_body_length
571
- class func createViews(mDB: Database, views: [JsonView]) throws -> Int {
559
+ class func createViews(mDB: Database, views: [ImportView]) throws -> Int {
572
560
  var changes: Int = 0
573
561
  var initChanges: Int = -1
574
562
  var isView: Bool = false
@@ -631,7 +619,7 @@ class ImportFromJson {
631
619
  return changes
632
620
 
633
621
  }
634
- class func createView(mDB: Database, view: JsonView) throws {
622
+ class func createView(mDB: Database, view: ImportView) throws {
635
623
  let stmt = "CREATE VIEW IF NOT EXISTS \(view.name) AS \(view.value);"
636
624
  do {
637
625
  try UtilsSQLCipher.execute(mDB: mDB, sql: stmt)
@@ -7,6 +7,13 @@
7
7
  //
8
8
 
9
9
  import Foundation
10
+
11
+ public struct EncryptJson: Codable {
12
+ let expData: String
13
+ public func show() {
14
+ print("expData: \(expData) ")
15
+ }
16
+ }
10
17
  public struct JsonSQLite: Codable {
11
18
  let database: String
12
19
  let version: Int
@@ -39,80 +39,80 @@ class UtilsDownloadFromHTTP {
39
39
  isDirectory: false
40
40
  )
41
41
 
42
- let task = URLSession.shared.downloadTask(with: mUrl) {
43
- (tempURL, response, error) in
44
- // Early exit on error
45
- guard let tempURL = tempURL else {
46
- let msg = "\(String(describing: error?.localizedDescription))"
47
- print("\(msg)")
48
- completion(.failure(UtilsDownloadError.downloadFromHTTPFailed))
49
- return
50
- }
51
- if let httpResponse = response as? HTTPURLResponse {
52
- switch httpResponse.statusCode {
53
- case 200:
54
- do {
55
- // Remove any existing document at file
56
- if FileManager.default.fileExists(
57
- atPath: fileCacheURL.path) {
58
- try FileManager.default.removeItem(
59
- atPath: fileCacheURL.path)
60
- }
61
-
62
- // Copy the tempURL to file
63
- try FileManager.default.copyItem(
64
- at: tempURL,
65
- to: fileCacheURL
66
- )
67
- // Delete the tempUrl file
68
- try FileManager.default.removeItem(at: tempURL)
69
- let dbURL = try UtilsFile.getDatabaseLocationURL(
70
- databaseLocation: databaseLocation)
71
- if isZip {
72
- // get the zip files
73
- let zipList: [String] = try UtilsFile
74
- .getFileList(path: cacheURL.path,
75
- ext: ".zip")
76
- // loop through the database files
77
- for zip in zipList {
78
- _ = try UtilsFile.unzipToDatabase(
79
- fromURL: cacheURL,
80
- databaseLocation: tmp,
81
- zip: zip,
82
- overwrite: true)
42
+ let task = URLSession.shared
43
+ .downloadTask(with: mUrl) {(tempURL, response, error) in
44
+ // Early exit on error
45
+ guard let tempURL = tempURL else {
46
+ let msg = "\(String(describing: error?.localizedDescription))"
47
+ print("\(msg)")
48
+ completion(.failure(UtilsDownloadError.downloadFromHTTPFailed))
49
+ return
50
+ }
51
+ if let httpResponse = response as? HTTPURLResponse {
52
+ switch httpResponse.statusCode {
53
+ case 200:
54
+ do {
55
+ // Remove any existing document at file
56
+ if FileManager.default.fileExists(
57
+ atPath: fileCacheURL.path) {
58
+ try FileManager.default.removeItem(
59
+ atPath: fileCacheURL.path)
83
60
  }
84
- // Delete the zip file
85
- try FileManager.default.removeItem(
86
- at: fileCacheURL)
87
61
 
62
+ // Copy the tempURL to file
63
+ try FileManager.default.copyItem(
64
+ at: tempURL,
65
+ to: fileCacheURL
66
+ )
67
+ // Delete the tempUrl file
68
+ try FileManager.default.removeItem(at: tempURL)
69
+ let dbURL = try UtilsFile.getDatabaseLocationURL(
70
+ databaseLocation: databaseLocation)
71
+ if isZip {
72
+ // get the zip files
73
+ let zipList: [String] = try UtilsFile
74
+ .getFileList(path: cacheURL.path,
75
+ ext: ".zip")
76
+ // loop through the database files
77
+ for zip in zipList {
78
+ _ = try UtilsFile.unzipToDatabase(
79
+ fromURL: cacheURL,
80
+ databaseLocation: tmp,
81
+ zip: zip,
82
+ overwrite: true)
83
+ }
84
+ // Delete the zip file
85
+ try FileManager.default.removeItem(
86
+ at: fileCacheURL)
87
+
88
+ }
89
+ try UtilsFile.moveAllDBSQLite(
90
+ fromURL: cacheURL,
91
+ dirUrl: dbURL)
92
+ completion(.success(true))
93
+ return
88
94
  }
89
- try UtilsFile.moveAllDBSQLite(
90
- fromURL: cacheURL,
91
- dirUrl: dbURL)
92
- completion(.success(true))
93
- return
94
- }
95
95
 
96
- // Handle potential file system errors
97
- catch let error {
98
- let msg = "\(error.localizedDescription)"
96
+ // Handle potential file system errors
97
+ catch let error {
98
+ let msg = "\(error.localizedDescription)"
99
+ print("\(msg)")
100
+ completion(.failure(UtilsDownloadError.downloadFromHTTPFailed))
101
+ return
102
+ }
103
+ default:
104
+ let msg = "Download: GET resquest not successful. http status code \(httpResponse.statusCode)"
99
105
  print("\(msg)")
100
106
  completion(.failure(UtilsDownloadError.downloadFromHTTPFailed))
101
107
  return
102
108
  }
103
- default:
104
- let msg = "Download: GET resquest not successful. http status code \(httpResponse.statusCode)"
109
+ } else {
110
+ let msg = "Download: not a valid http response"
105
111
  print("\(msg)")
106
112
  completion(.failure(UtilsDownloadError.downloadFromHTTPFailed))
107
113
  return
108
114
  }
109
- } else {
110
- let msg = "Download: not a valid http response"
111
- print("\(msg)")
112
- completion(.failure(UtilsDownloadError.downloadFromHTTPFailed))
113
- return
114
115
  }
115
- }
116
116
  // Start the download
117
117
  task.resume()
118
118
  }
@@ -233,7 +233,8 @@ class UtilsDrop {
233
233
  retChanges = try self.dropViews(mDB: mDB)
234
234
  if changes >= 0 {
235
235
  _ = try UtilsSQLCipher.prepareSQL(mDB: mDB, sql: "VACUUM;",
236
- values: [], fromJson: false)
236
+ values: [], fromJson: false,
237
+ returnMode: "no")
237
238
  changes = UtilsSQLCipher.dbChanges(mDB: mDB.mDb) -
238
239
  initChanges
239
240
  }
@@ -6,6 +6,8 @@
6
6
  //
7
7
 
8
8
  import Foundation
9
+ import CryptoKit
10
+ import CommonCrypto
9
11
 
10
12
  enum UtilsJsonError: Error {
11
13
  case tableNotExists(message: String)
@@ -20,7 +22,12 @@ enum UtilsJsonError: Error {
20
22
  case isLastModified(message: String)
21
23
  case isSqlDeleted(message: String)
22
24
  case checkUpdate(message: String)
23
- case checkValues(message: String)}
25
+ case checkValues(message: String)
26
+ case encryptDictionaryToBase64(message: String)
27
+ case decryptBase64ToDictionary(message: String)
28
+ }
29
+
30
+ var mSalt = "jeep_capacitor_sqlite"
24
31
 
25
32
  // swiftlint:disable file_length
26
33
  // swiftlint:disable type_body_length
@@ -466,6 +473,121 @@ class UtilsJson {
466
473
  return isViews
467
474
  }
468
475
 
476
+ // MARK: encryptDictionaryToBase64
477
+
478
+ class func encryptDictionaryToBase64(_ dictionary: [String: Any], forAccount account: String) throws -> String {
479
+ // Encryption using Swift Crypto and Keychain-stored passphrase
480
+
481
+ let jsonData = try JSONSerialization.data(withJSONObject: dictionary)
482
+
483
+ let isPassPhrase = try UtilsSecret.isPassphrase(account: account)
484
+ if !isPassPhrase {
485
+ throw UtilsJsonError.encryptDictionaryToBase64(
486
+ message: "No passphrase stored")
487
+ }
488
+ let passphrase = UtilsSecret.getPassphrase(account: account)
489
+ // Generate a constant salt
490
+ let salt = Data(mSalt.utf8)
491
+
492
+ // Derive the encryption key using the passphrase and salt
493
+ guard let key = deriveSymmetricKeyFromPassphrase(passphrase, salt: salt) else {
494
+ throw UtilsJsonError.encryptDictionaryToBase64(
495
+ message: "No Encryption key returned")
496
+ }
497
+ let symKey = SymmetricKey(data: key)
498
+
499
+ // Use Swift Crypto to perform AES encryption with GCM mode
500
+ let sealedBox = try AES.GCM.seal(jsonData, using: symKey, nonce: AES.GCM.Nonce())
501
+
502
+ if let combined = sealedBox.combined {
503
+ // combined the salt and the encrypted data
504
+ var saltAndEncryptedData = salt
505
+ saltAndEncryptedData.append(combined)
506
+ // Convert to base64 string
507
+
508
+ let base64String = saltAndEncryptedData.base64EncodedString()
509
+ return base64String
510
+ } else {
511
+ throw UtilsJsonError.encryptDictionaryToBase64(
512
+ message: "Conversion to base64String failed")
513
+
514
+ }
515
+
516
+ }
517
+
518
+ // MARK: decrypt DictionaryToBase64
519
+
520
+ class func decryptBase64ToDictionary(_ base64String: String,
521
+ forAccount account: String)
522
+ throws -> [String: Any] {
523
+ guard let data = Data(base64Encoded: base64String) else {
524
+ throw UtilsJsonError.decryptBase64ToDictionary(
525
+ message: "Conversion from Base64 to Dictionary failed")
526
+
527
+ }
528
+ let isPassPhrase = try UtilsSecret.isPassphrase(account: account)
529
+ if !isPassPhrase {
530
+ throw UtilsJsonError.decryptBase64ToDictionary(
531
+ message: "No passphrase stored")
532
+ }
533
+ let passphrase = UtilsSecret.getPassphrase(account: account)
534
+ // Generate a constant salt
535
+ let salt = Data(mSalt.utf8)
536
+
537
+ // Derive the encryption key using the passphrase and salt
538
+ guard let key = deriveSymmetricKeyFromPassphrase(passphrase,
539
+ salt: salt) else {
540
+ throw UtilsJsonError.encryptDictionaryToBase64(
541
+ message: "No Encryption key returned")
542
+ }
543
+ let symKey = SymmetricKey(data: key)
544
+
545
+ // Extract the encrypted data from the remaining bytes
546
+ let encryptedData = data.suffix(from: salt.count)
547
+
548
+ // Convert the base64 string to a sealed box
549
+ let sealedBox = try AES.GCM.SealedBox(combined: encryptedData)
550
+
551
+ // Decrypt the ciphertext using AES GCM open
552
+ let decryptedData = try AES.GCM.open(sealedBox, using: symKey)
553
+
554
+ // Convert decrypted data to dictionary
555
+ let dictionary = try JSONSerialization.jsonObject(
556
+ with: decryptedData) as? [String: Any] ?? [:]
557
+
558
+ return dictionary
559
+ }
560
+
561
+ // MARK: deriveSymmetricKeyFromPassphrase
562
+
563
+ class func deriveSymmetricKeyFromPassphrase(_ passphrase: String,
564
+ salt: Data)
565
+ -> Data? {
566
+
567
+ let passphraseData = Data(passphrase.utf8)
568
+ let keyLength = kCCKeySizeAES256
569
+ let iterations: UInt32 = 10000
570
+ var derivedKeyData = Data(count: keyLength)
571
+ let derivedCount = derivedKeyData.count
572
+ let derivationStatus = derivedKeyData.withUnsafeMutableBytes { derivedKeyUnsafeMutableRawBufferPointer in
573
+ passphraseData.withUnsafeBytes { passphraseUnsafeRawBufferPointer in
574
+ salt.withUnsafeBytes { saltUnsafeRawBufferPointer in
575
+ CCKeyDerivationPBKDF(
576
+ CCPBKDFAlgorithm(kCCPBKDF2),
577
+ passphraseUnsafeRawBufferPointer.baseAddress,
578
+ passphraseData.count,
579
+ saltUnsafeRawBufferPointer.baseAddress,
580
+ salt.count,
581
+ CCPseudoRandomAlgorithm(kCCPRFHmacAlgSHA256),
582
+ iterations,
583
+ derivedKeyUnsafeMutableRawBufferPointer.baseAddress,
584
+ derivedCount
585
+ )
586
+ }
587
+ }
588
+ }
589
+ return derivationStatus == kCCSuccess ? derivedKeyData : nil
590
+ }
469
591
  }
470
592
  // swiftlint:enable type_body_length
471
593
  // swiftlint:enable file_length