@capacitor-community/sqlite 5.0.5 → 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.
@@ -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
@@ -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
@@ -1301,7 +1301,6 @@ class UtilsSQLCipher {
1301
1301
  return true // Keep dictionaries without any keys
1302
1302
  }
1303
1303
  return !keysInArray1.contains(dict2Key)
1304
-
1305
1304
  }
1306
1305
  }
1307
1306
  retResponse.append(contentsOf: mRespSet)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@capacitor-community/sqlite",
3
- "version": "5.0.5",
3
+ "version": "5.0.6",
4
4
  "description": "Community plugin for native & electron SQLite databases",
5
5
  "main": "dist/plugin.cjs.js",
6
6
  "module": "dist/esm/index.js",
@@ -120,6 +120,20 @@ export interface CapacitorSQLitePlugin {
120
120
  * @since 0.0.1
121
121
  */
122
122
  close(options: capSQLiteOptions): Promise<void>;
123
+ /**
124
+ * Load a SQlite extension
125
+ * @param options :capSQLiteExtensionPath
126
+ * @returns Promise<void>
127
+ * @since 5.0.6
128
+ */
129
+ // loadExtension(options: capSQLiteExtensionPath): Promise<void>;
130
+ /**
131
+ * Enable Or Disable Extension Loading
132
+ * @param options
133
+ * @returns Promise<void>
134
+ * @since 5.0.6
135
+ */
136
+ // enableLoadExtension(options: capSQLiteExtensionEnable): Promise<void>;
123
137
  /**
124
138
  * GetUrl get the database Url
125
139
  * @param options: capSQLiteOptions
@@ -391,6 +405,38 @@ export interface capEchoOptions {
391
405
  */
392
406
  value?: string;
393
407
  }
408
+ export interface capSQLiteExtensionPath {
409
+ /**
410
+ * The database name
411
+ */
412
+ database?: string;
413
+ /**
414
+ * The extension path
415
+ */
416
+ path?: string;
417
+ /**
418
+ * ReadOnly / ReadWrite
419
+ * default ReadWrite (false)
420
+ * @since 4.1.0-7
421
+ */
422
+ readonly?: boolean;
423
+ }
424
+ export interface capSQLiteExtensionEnable {
425
+ /**
426
+ * The database name
427
+ */
428
+ database?: string;
429
+ /**
430
+ * The enabling toggle (1: ON, 0: OFF)
431
+ */
432
+ toggle?: boolean;
433
+ /**
434
+ * ReadOnly / ReadWrite
435
+ * default ReadWrite (false)
436
+ * @since 4.1.0-7
437
+ */
438
+ readonly?: boolean;
439
+ }
394
440
  export interface capConnectionOptions {
395
441
  /**
396
442
  * The database name
@@ -436,6 +482,7 @@ export interface capSQLiteOptions {
436
482
  */
437
483
  readonly?: boolean;
438
484
  }
485
+
439
486
  export interface capNCDatabasePathOptions {
440
487
  /**
441
488
  * the database path
@@ -769,6 +816,12 @@ export interface capSQLiteSyncDate {
769
816
  }
770
817
 
771
818
  /* JSON Types */
819
+ export interface EncryptJson {
820
+ /**
821
+ * The encrypted JsonSQLite base64 string
822
+ */
823
+ expData: string;
824
+ }
772
825
  export interface JsonSQLite {
773
826
  /**
774
827
  * The database name
@@ -1699,6 +1752,20 @@ export interface ISQLiteDBConnection {
1699
1752
  * @since 3.2.0
1700
1753
  */
1701
1754
  getVersion(): Promise<capVersionResult>;
1755
+ /**
1756
+ * Load a SQlite extension
1757
+ * @param path :SQlite extension path
1758
+ * @returns Promise<void>
1759
+ * @since 5.0.6
1760
+ */
1761
+ loadExtension(path: string): Promise<void>;
1762
+ /**
1763
+ * Enable Or Disable Extension Loading
1764
+ * @param toggle true:on false:off
1765
+ * @returns Promise<void>
1766
+ * @since 5.0.6
1767
+ */
1768
+ enableLoadExtension(toggle: boolean): Promise<void>;
1702
1769
  /**
1703
1770
  * Execute SQLite DB Connection Statements
1704
1771
  * @param statements
@@ -1849,6 +1916,43 @@ export class SQLiteDBConnection implements ISQLiteDBConnection {
1849
1916
  return Promise.reject(err);
1850
1917
  }
1851
1918
  }
1919
+
1920
+ async loadExtension(path: string): Promise<void> {
1921
+ try {
1922
+ console.log(`database: ${this.dbName}`);
1923
+ console.log(`readonly: ${this.readonly}}`);
1924
+ console.log(`path: ${path}}`);
1925
+ await this.sqlite.loadExtension({
1926
+ database: this.dbName,
1927
+ path: path,
1928
+ readonly: this.readonly,
1929
+ });
1930
+ console.log(`loadExtension successful`);
1931
+ return Promise.resolve();
1932
+ } catch (err) {
1933
+ console.log(`loadExtension failed `);
1934
+ return Promise.reject(err);
1935
+ }
1936
+ }
1937
+ async enableLoadExtension(toggle: boolean): Promise<void> {
1938
+ try {
1939
+ console.log(`database: ${this.dbName}`);
1940
+ console.log(`readonly: ${this.readonly}`);
1941
+ console.log(`toggle: ${toggle}`);
1942
+ await this.sqlite.enableLoadExtension({
1943
+ database: this.dbName,
1944
+ toggle: toggle,
1945
+ readonly: this.readonly,
1946
+ });
1947
+ console.log(`enableLoadExtension successful`);
1948
+ return Promise.resolve();
1949
+ } catch (err) {
1950
+ console.log(err);
1951
+ console.log(`enableLoadExtension failed `);
1952
+ return Promise.reject(err);
1953
+ }
1954
+ }
1955
+
1852
1956
  async getUrl(): Promise<capSQLiteUrl> {
1853
1957
  try {
1854
1958
  const res: capSQLiteUrl = await this.sqlite.getUrl({
package/src/web.ts CHANGED
@@ -32,6 +32,8 @@ import type {
32
32
  capSQLiteUrl,
33
33
  capSQLiteValues,
34
34
  capVersionResult,
35
+ capSQLiteExtensionPath,
36
+ capSQLiteExtensionEnable,
35
37
  } from './definitions';
36
38
 
37
39
  export class CapacitorSQLiteWeb
@@ -591,4 +593,12 @@ export class CapacitorSQLiteWeb
591
593
  async isInConfigBiometricAuth(): Promise<capSQLiteResult> {
592
594
  throw this.unimplemented('Not implemented on web.');
593
595
  }
596
+ async loadExtension(options: capSQLiteExtensionPath): Promise<void> {
597
+ console.log('loadExtension', options);
598
+ throw this.unimplemented('Not implemented on web.');
599
+ }
600
+ async enableLoadExtension(options: capSQLiteExtensionEnable): Promise<void> {
601
+ console.log('enableLoadExtension', options);
602
+ throw this.unimplemented('Not implemented on web.');
603
+ }
594
604
  }