expo-device-crypto 0.2.3 → 0.3.0

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.
package/README.md CHANGED
@@ -28,25 +28,28 @@ If you want to allow Face ID on iOS, add this to your app config:
28
28
 
29
29
  ### Signature Algorithms
30
30
 
31
- - #### `ECDSA_SECP256R1_SHA256`
31
+ - `ECDSA_SECP256R1_SHA256`
32
32
 
33
33
  **Curve:** P-256 / secp256r1. </br>
34
34
  **Hash:** SHA-256. </br>
35
- **Use it for:** Authentication challenges, message signing, and tamper detection.
36
35
 
37
36
  ### Encryption Algorithms
38
37
 
39
- - #### `RSA_2048_PKCS1`
38
+ - `RSA_2048_PKCS1`
40
39
 
41
40
  **Key size:** 2048 bits. </br>
42
41
  **Padding:** PKCS#1 v1.5. </br>
43
- **Use it for:** Encrypting small secrets that only the private key holder should decrypt.
44
42
 
45
- - #### `RSA_2048_OAEP_SHA1`
43
+ - `RSA_2048_OAEP_SHA1`
46
44
 
47
45
  **Key size:** 2048 bits. </br>
48
46
  **Padding:** OAEP with SHA-1 and MGF1. </br>
49
- **Use it for:** Encrypting small secrets with stronger padding than PKCS#1 v1.5 when interoperating with OAEP SHA-1 systems.
47
+
48
+ - `ECIES_P256_AES256_GCM`
49
+
50
+ **Curve:** P-256 (secp256r1). </br>
51
+ **Symmetric cipher:** AES-256-GCM. </br>
52
+ **Key derivation:** HKDF-SHA256 (32-byte key from ECDH shared secret). </br>
50
53
 
51
54
  **More coming soon...**
52
55
 
@@ -171,18 +174,18 @@ const decrypted = await DeviceCrypto.decrypt(alias, encrypted ?? "", {
171
174
  - `encrypt(alias: string, data: string, options?: EncryptOptions): Promise<string | null>`
172
175
  - Encrypts UTF-8 `data` with public key.
173
176
  - Default: `algorithmType = RSA_2048_PKCS1`.
177
+ - For `algorithmType = ECIES_P256_AES256_GCM`, `peerPublicKey` is required in `options`.
174
178
 
175
179
  - `decrypt(alias: string, data: string, options?: DecryptOptions): Promise<string | null>`
176
180
  - Decrypts Base64 `data` with private key.
177
181
  - Defaults: `algorithmType = RSA_2048_PKCS1`, `promptTitle = "Unlock"`, `promptSubtitle = "Enter your PIN to continue"`, `authMethod = PASSCODE_OR_BIOMETRIC`.
182
+ - For `algorithmType = ECIES_P256_AES256_GCM`, `peerPublicKey` is required in `options`.
178
183
 
179
184
  **Android only methods**
180
185
 
181
186
  - `isStrongBoxAvailable(): boolean`
182
187
  - Returns `true` if StrongBox Keystore is supported on the device.
183
188
 
184
- > ✅ JSDoc type definitions are available in `./src/DeviceCryptoModule.ts`.
185
-
186
189
  ## Important
187
190
 
188
191
  ### Enable Strong Box on Android
@@ -205,6 +208,6 @@ await DeviceCrypto.generateKeyPair(alias, {
205
208
 
206
209
  If StrongBox is not available, Android falls back to the Trusted Execution Environment (TEE).
207
210
 
208
- ### RSA algorithms on iOS
211
+ ### Secure Enclave support on iOS
209
212
 
210
- Please note that RSA private-key operations are not performed in the [Secure Enclave processor](https://developer.apple.com/documentation/security/ksecattrtokenidsecureenclave?utm_source=chatgpt.com#Discussion). Instead, they are software-backed and handled by Apple’s system cryptographic services, which still provide strong isolation and protection.
213
+ On iOS, only ECDSA keys can use the [Secure Enclave processor](https://developer.apple.com/documentation/security/ksecattrtokenidsecureenclave?utm_source=chatgpt.com#Discussion). RSA private-key operations are software-backed and handled by Apple’s system cryptographic services, which still provide strong isolation and protection.
@@ -1,7 +1,7 @@
1
1
  apply plugin: 'com.android.library'
2
2
 
3
3
  group = 'expo.modules.devicecrypto'
4
- version = '0.2.3'
4
+ version = '0.3.0'
5
5
 
6
6
  def expoModulesCorePlugin = new File(project(":expo-modules-core").projectDir.absolutePath, "ExpoModulesCorePlugin.gradle")
7
7
  apply from: expoModulesCorePlugin
@@ -35,7 +35,7 @@ android {
35
35
  namespace "expo.modules.devicecrypto"
36
36
  defaultConfig {
37
37
  versionCode 1
38
- versionName "0.2.3"
38
+ versionName "0.3.0"
39
39
  }
40
40
  lintOptions {
41
41
  abortOnError false
@@ -16,6 +16,7 @@ import android.security.keystore.KeyProperties.PURPOSE_DECRYPT
16
16
  import android.security.keystore.KeyProperties.PURPOSE_ENCRYPT
17
17
  import android.security.keystore.KeyProperties.PURPOSE_SIGN
18
18
  import android.security.keystore.KeyProperties.PURPOSE_VERIFY
19
+ import android.security.keystore.KeyProperties.PURPOSE_AGREE_KEY
19
20
  import android.util.Base64
20
21
  import androidx.biometric.BiometricManager
21
22
  import androidx.biometric.BiometricManager.Authenticators.BIOMETRIC_STRONG
@@ -32,9 +33,14 @@ import java.security.KeyStore
32
33
  import java.security.Signature
33
34
  import java.security.spec.AlgorithmParameterSpec
34
35
  import java.security.spec.MGF1ParameterSpec
36
+ import java.security.spec.X509EncodedKeySpec
35
37
  import javax.crypto.Cipher
38
+ import javax.crypto.KeyAgreement
39
+ import javax.crypto.Mac
40
+ import javax.crypto.spec.GCMParameterSpec
36
41
  import javax.crypto.spec.OAEPParameterSpec
37
42
  import javax.crypto.spec.PSource
43
+ import javax.crypto.spec.SecretKeySpec
38
44
 
39
45
 
40
46
  enum class GenerateKeyPairResult {
@@ -58,6 +64,7 @@ enum class AlgorithmType {
58
64
  ECDSA_SECP256R1_SHA256,
59
65
  RSA_2048_PKCS1,
60
66
  RSA_2048_OAEP_SHA1,
67
+ ECIES_P256_AES256_GCM,
61
68
  }
62
69
 
63
70
  class DeviceCryptoModule : Module() {
@@ -66,9 +73,46 @@ class DeviceCryptoModule : Module() {
66
73
  AlgorithmType.ECDSA_SECP256R1_SHA256 -> "SHA256withECDSA"
67
74
  AlgorithmType.RSA_2048_PKCS1 -> "RSA/ECB/PKCS1Padding"
68
75
  AlgorithmType.RSA_2048_OAEP_SHA1 -> "RSA/ECB/OAEPwithSHA-1AndMGF1Padding"
76
+ AlgorithmType.ECIES_P256_AES256_GCM -> "AES/GCM/NoPadding"
77
+ else -> throw Exception("INVALID_ALGORITHM_TYPE")
69
78
  }
70
79
  }
71
80
 
81
+ private fun hkdfSha256(
82
+ ikm: ByteArray,
83
+ length: Int,
84
+ salt: ByteArray? = null,
85
+ info: ByteArray? = null): ByteArray {
86
+ val mac = Mac.getInstance("HmacSHA256")
87
+ val actualSalt = salt ?: ByteArray(32)
88
+ mac.init(SecretKeySpec(actualSalt, "HmacSHA256"))
89
+ val prk = mac.doFinal(ikm)
90
+
91
+ val result = ByteArray(length)
92
+ var t = ByteArray(0)
93
+ var offset = 0
94
+ var counter = 1
95
+
96
+ while (offset < length) {
97
+ val macExpand = Mac.getInstance("HmacSHA256")
98
+ macExpand.init(SecretKeySpec(prk, "HmacSHA256"))
99
+
100
+ macExpand.update(t)
101
+ if (info != null) macExpand.update(info)
102
+ macExpand.update(counter.toByte())
103
+
104
+ t = macExpand.doFinal()
105
+
106
+ val toCopy = minOf(t.size, length - offset)
107
+ System.arraycopy(t, 0, result, offset, toCopy)
108
+
109
+ offset += toCopy
110
+ counter++
111
+ }
112
+
113
+ return result
114
+ }
115
+
72
116
  private fun getOaepSpec(algoType: AlgorithmType): AlgorithmParameterSpec? {
73
117
  return when (algoType) {
74
118
  AlgorithmType.RSA_2048_OAEP_SHA1 -> OAEPParameterSpec(
@@ -107,7 +151,7 @@ class DeviceCryptoModule : Module() {
107
151
 
108
152
  private fun buildECDSA(
109
153
  alias: String,
110
- digest: String,
154
+ digest: String?,
111
155
  reqAuth: Boolean,
112
156
  strongBox: Boolean): KeyPairGenerator {
113
157
  val kpg: KeyPairGenerator = KeyPairGenerator.getInstance(
@@ -119,7 +163,9 @@ class DeviceCryptoModule : Module() {
119
163
  alias,
120
164
  PURPOSE_SIGN or PURPOSE_VERIFY
121
165
  ).run {
122
- setDigests(digest)
166
+ if (digest != null) {
167
+ setDigests(digest)
168
+ }
123
169
  setIsStrongBoxBacked(strongBox)
124
170
  setUserAuthenticationRequired(reqAuth)
125
171
  if (reqAuth) {
@@ -167,6 +213,37 @@ class DeviceCryptoModule : Module() {
167
213
  return kpg
168
214
  }
169
215
 
216
+ private fun buildECIES(
217
+ alias: String,
218
+ digest: String?,
219
+ reqAuth: Boolean,
220
+ strongBox: Boolean): KeyPairGenerator {
221
+ val kpg: KeyPairGenerator = KeyPairGenerator.getInstance(
222
+ KEY_ALGORITHM_EC,
223
+ "AndroidKeyStore"
224
+ )
225
+
226
+ val parameterSpec: KeyGenParameterSpec = KeyGenParameterSpec.Builder(
227
+ alias,
228
+ PURPOSE_AGREE_KEY
229
+ ).run {
230
+ if (digest != null) {
231
+ setDigests(digest)
232
+ }
233
+ setIsStrongBoxBacked(strongBox)
234
+ setUserAuthenticationRequired(reqAuth)
235
+ if (reqAuth) {
236
+ setUserAuthenticationParameters(
237
+ 30, // 30 seconds
238
+ AUTH_BIOMETRIC_STRONG or AUTH_DEVICE_CREDENTIAL
239
+ )
240
+ }
241
+ build()
242
+ }
243
+
244
+ kpg.initialize(parameterSpec)
245
+ return kpg
246
+ }
170
247
 
171
248
  private fun showAuthPrompt(
172
249
  onSuccess: () -> Unit,
@@ -249,6 +326,7 @@ class DeviceCryptoModule : Module() {
249
326
  AlgorithmType.ECDSA_SECP256R1_SHA256 -> buildECDSA(alias, DIGEST_SHA256, reqAuth, strongBox)
250
327
  AlgorithmType.RSA_2048_PKCS1 -> buildRSA(alias, null, ENCRYPTION_PADDING_RSA_PKCS1, reqAuth, strongBox)
251
328
  AlgorithmType.RSA_2048_OAEP_SHA1 -> buildRSA(alias, DIGEST_SHA1, ENCRYPTION_PADDING_RSA_OAEP, reqAuth, strongBox)
329
+ AlgorithmType.ECIES_P256_AES256_GCM -> buildECIES(alias, null, reqAuth, strongBox)
252
330
  else -> throw Exception("INVALID_ALGORITHM_TYPE")
253
331
  }
254
332
 
@@ -279,6 +357,20 @@ class DeviceCryptoModule : Module() {
279
357
  return Base64.encodeToString(publicKeyBytes, Base64.NO_WRAP)
280
358
  }
281
359
 
360
+ private fun deriveSharedSecret(entry: KeyStore.PrivateKeyEntry, publicKey: String): ByteArray {
361
+ val peerPublicKeyBytes = Base64.decode(publicKey, Base64.NO_WRAP)
362
+ val peerPublicKeySpec = X509EncodedKeySpec(peerPublicKeyBytes)
363
+ val peerPublicKey = KeyFactory
364
+ .getInstance(KEY_ALGORITHM_EC)
365
+ .generatePublic(peerPublicKeySpec)
366
+
367
+ return KeyAgreement.getInstance("ECDH", "AndroidKeyStore").run {
368
+ init(entry.privateKey)
369
+ doPhase(peerPublicKey, true)
370
+ generateSecret()
371
+ }
372
+ }
373
+
282
374
  private fun sign(alias: String, data: String, o: Map<String, Any?>, promise: Promise) {
283
375
  val entry = getKeyStoreEntry(alias)
284
376
  if (entry !is KeyStore.PrivateKeyEntry) {
@@ -294,24 +386,24 @@ class DeviceCryptoModule : Module() {
294
386
  val algoType = AlgorithmType.valueOf(o["algoType"] as String)
295
387
  val algo = toAndroidAlgo(algoType)
296
388
 
297
- if (!reqAuth) {
389
+ fun runSigning() {
298
390
  val signature: ByteArray = Signature.getInstance(algo).apply {
299
391
  initSign(entry.privateKey)
300
392
  update(data.toByteArray(Charsets.UTF_8))
301
393
  }.sign()
302
394
  promise.resolve(Base64.encodeToString(signature, Base64.NO_WRAP))
395
+ }
396
+
397
+ if (!reqAuth) {
398
+ runSigning()
303
399
  return
304
400
  } else {
305
401
  showAuthPrompt(
306
402
  onSuccess = {
307
- val signature: ByteArray = Signature.getInstance(algo).apply {
308
- initSign(entry.privateKey)
309
- update(data.toByteArray(Charsets.UTF_8))
310
- }.sign()
311
- promise.resolve(Base64.encodeToString(signature, Base64.NO_WRAP))
403
+ runSigning()
312
404
  },
313
405
  onError = { error ->
314
- promise.reject("ERR_AUTH_FAILED", error, null)
406
+ promise.reject("AUTH_FAILED", error, null)
315
407
  },
316
408
  title = title,
317
409
  subtitle = subtitle,
@@ -341,18 +433,62 @@ class DeviceCryptoModule : Module() {
341
433
  return
342
434
  }
343
435
 
436
+ val reqAuth = isKeyStoreRequireAuthentication(entry)
437
+
438
+ val title = o["title"] as String
439
+ val subtitle = o["subtitle"] as String
440
+ val authMethod = AuthMethod.valueOf(o["authMethod"] as String)
344
441
  val algoType = AlgorithmType.valueOf(o["algoType"] as String)
345
442
  val algo = toAndroidAlgo(algoType)
346
- val oaepSpec = getOaepSpec(algoType)
347
-
348
443
  val cipher = Cipher.getInstance(algo)
349
- if (oaepSpec != null) {
350
- cipher.init(Cipher.ENCRYPT_MODE, entry.certificate.publicKey, oaepSpec)
444
+
445
+ fun runEncryption(cipher: Cipher) {
446
+ when (algoType) {
447
+ AlgorithmType.RSA_2048_OAEP_SHA1 -> {
448
+ val oaepSpec = getOaepSpec(algoType)
449
+ cipher.init(Cipher.ENCRYPT_MODE, entry.certificate.publicKey, oaepSpec)
450
+ val encrypted = cipher.doFinal(data.toByteArray(Charsets.UTF_8))
451
+ promise.resolve(Base64.encodeToString(encrypted, Base64.NO_WRAP))
452
+ }
453
+ AlgorithmType.RSA_2048_PKCS1 -> {
454
+ cipher.init(Cipher.ENCRYPT_MODE, entry.certificate.publicKey)
455
+ val encrypted = cipher.doFinal(data.toByteArray(Charsets.UTF_8))
456
+ promise.resolve(Base64.encodeToString(encrypted, Base64.NO_WRAP))
457
+ }
458
+ AlgorithmType.ECIES_P256_AES256_GCM -> {
459
+ val peerPublicKey = o["peerPublicKey"] as String
460
+ val sharedSecret = deriveSharedSecret(entry, peerPublicKey)
461
+ val aesKey = SecretKeySpec(hkdfSha256(sharedSecret, 32), "AES")
462
+ cipher.init(Cipher.ENCRYPT_MODE, aesKey)
463
+ val encrypted = cipher.doFinal(data.toByteArray(Charsets.UTF_8))
464
+ val payload = cipher.iv + encrypted
465
+ promise.resolve(Base64.encodeToString(payload, Base64.NO_WRAP))
466
+ }
467
+ else -> {
468
+ throw Exception("INVALID_ALGORITHM_TYPE")
469
+ }
470
+ }
471
+ }
472
+
473
+ if (algoType != AlgorithmType.ECIES_P256_AES256_GCM) {
474
+ runEncryption(cipher)
475
+ return
476
+ } else if (!reqAuth) {
477
+ runEncryption(cipher)
478
+ return
351
479
  } else {
352
- cipher.init(Cipher.ENCRYPT_MODE, entry.certificate.publicKey)
480
+ showAuthPrompt(
481
+ onSuccess = {
482
+ runEncryption(cipher)
483
+ },
484
+ onError = { error ->
485
+ promise.reject("AUTH_FAILED", error, null)
486
+ },
487
+ title = title,
488
+ subtitle = subtitle,
489
+ authMethod = authMethod
490
+ )
353
491
  }
354
- val encrypted: ByteArray = cipher.doFinal(data.toByteArray(Charsets.UTF_8))
355
- promise.resolve(Base64.encodeToString(encrypted, Base64.NO_WRAP))
356
492
  }
357
493
 
358
494
  private fun decrypt(alias: String, data: String, o: Map<String, Any?>, promise: Promise) {
@@ -369,33 +505,53 @@ class DeviceCryptoModule : Module() {
369
505
  val authMethod = o["authMethod"] as String
370
506
  val algoType = AlgorithmType.valueOf(o["algoType"] as String)
371
507
  val algo = toAndroidAlgo(algoType)
372
- val oaepSpec = getOaepSpec(algoType)
373
508
 
374
509
  val cipher = Cipher.getInstance(algo)
375
510
  val ciphertext = Base64.decode(data, Base64.NO_WRAP)
376
511
 
377
- if (!reqAuth) {
378
- if (oaepSpec != null) {
379
- cipher.init(Cipher.DECRYPT_MODE, entry.privateKey, oaepSpec)
380
- } else {
381
- cipher.init(Cipher.DECRYPT_MODE, entry.privateKey)
512
+ fun runDecryption(cipher: Cipher) {
513
+ when (algoType) {
514
+ AlgorithmType.RSA_2048_OAEP_SHA1 -> {
515
+ val oaepSpec = getOaepSpec(algoType)
516
+ cipher.init(Cipher.DECRYPT_MODE, entry.privateKey, oaepSpec)
517
+ val decrypted = cipher.doFinal(ciphertext)
518
+ promise.resolve(String(decrypted, Charsets.UTF_8))
519
+ return
520
+ }
521
+ AlgorithmType.RSA_2048_PKCS1 -> {
522
+ cipher.init(Cipher.DECRYPT_MODE, entry.privateKey)
523
+ val decrypted = cipher.doFinal(ciphertext)
524
+ promise.resolve(String(decrypted, Charsets.UTF_8))
525
+ return
526
+ }
527
+ AlgorithmType.ECIES_P256_AES256_GCM -> {
528
+ val peerPublicKey = o["peerPublicKey"] as String
529
+ val sharedSecret = deriveSharedSecret(entry, peerPublicKey)
530
+ val aesKey = SecretKeySpec(hkdfSha256(sharedSecret, 32), "AES")
531
+ val iv = ciphertext.copyOfRange(0, 12)
532
+ val encryptedPayload = ciphertext.copyOfRange(12, ciphertext.size)
533
+ val gcmSpec = GCMParameterSpec(128, iv)
534
+ cipher.init(Cipher.DECRYPT_MODE, aesKey, gcmSpec)
535
+ val decrypted = cipher.doFinal(encryptedPayload)
536
+ promise.resolve(String(decrypted, Charsets.UTF_8))
537
+ return
538
+ }
539
+ else -> {
540
+ throw Exception("INVALID_ALGORITHM_TYPE")
541
+ }
382
542
  }
383
- val decrypted: ByteArray = cipher.doFinal(ciphertext)
384
- promise.resolve(String(decrypted, Charsets.UTF_8))
543
+ }
544
+
545
+ if (!reqAuth) {
546
+ runDecryption(cipher)
385
547
  return
386
548
  } else {
387
549
  showAuthPrompt(
388
550
  onSuccess = {
389
- if (oaepSpec != null) {
390
- cipher.init(Cipher.DECRYPT_MODE, entry.privateKey, oaepSpec)
391
- } else {
392
- cipher.init(Cipher.DECRYPT_MODE, entry.privateKey)
393
- }
394
- val decrypted: ByteArray = cipher.doFinal(ciphertext)
395
- promise.resolve(String(decrypted, Charsets.UTF_8))
551
+ runDecryption(cipher)
396
552
  },
397
553
  onError = { error ->
398
- promise.reject("ERR_AUTH_FAILED", error, null)
554
+ promise.reject("AUTH_FAILED", error, null)
399
555
  },
400
556
  title = title,
401
557
  subtitle = subtitle,
@@ -15,11 +15,14 @@ export declare enum AuthMethod {
15
15
  export declare enum SigningAlgorithm {
16
16
  ECDSA_SECP256R1_SHA256 = "ECDSA_SECP256R1_SHA256"
17
17
  }
18
+ type EciesAlgorithm = "ECIES_P256_AES256_GCM";
19
+ type RsaAlgorithm = "RSA_2048_PKCS1" | "RSA_2048_OAEP_SHA1";
18
20
  export declare enum EncryptionAlgorithm {
19
21
  RSA_2048_PKCS1 = "RSA_2048_PKCS1",
20
- RSA_2048_OAEP_SHA1 = "RSA_2048_OAEP_SHA1"
22
+ RSA_2048_OAEP_SHA1 = "RSA_2048_OAEP_SHA1",
23
+ ECIES_P256_AES256_GCM = "ECIES_P256_AES256_GCM"
21
24
  }
22
- export interface GenerateKeyPairOptions {
25
+ export type GenerateKeyPairOptions = {
23
26
  /**
24
27
  * The algorithm type of the key to generate.
25
28
  */
@@ -55,8 +58,8 @@ export interface GenerateKeyPairOptions {
55
58
  * @platform android 🤖
56
59
  */
57
60
  preferStrongBox?: boolean;
58
- }
59
- interface BaseSignDecryptOptions {
61
+ };
62
+ type BaseAuthOptions = {
60
63
  /**
61
64
  * The title of the prompt to show when authentication is required.
62
65
  * @default "Unlock"
@@ -76,33 +79,42 @@ interface BaseSignDecryptOptions {
76
79
  * @platform android 🤖
77
80
  */
78
81
  authMethod?: AuthMethod;
79
- }
80
- interface BaseSigningOptions {
82
+ };
83
+ type BaseSigningOptions = {
81
84
  /**
82
85
  * The algorithm type of the key to use for signing.
83
86
  */
84
- algorithmType?: SigningAlgorithm;
85
- }
86
- interface BaseEncryptionOptions {
87
+ algorithmType: SigningAlgorithm;
88
+ };
89
+ type BaseVerifyOptions = BaseSigningOptions;
90
+ type BaseRsaEncryptionOptions = {
87
91
  /**
88
92
  * The algorithm type of the key to use for encrypting.
89
93
  */
90
- algorithmType?: EncryptionAlgorithm;
91
- }
92
- export interface SignOptions extends BaseSignDecryptOptions, BaseSigningOptions {
93
- }
94
- export interface VerifyOptions extends BaseSigningOptions {
95
- }
96
- export interface DecryptOptions extends BaseSignDecryptOptions, BaseEncryptionOptions {
97
- }
98
- export interface EncryptOptions extends BaseEncryptionOptions {
99
- }
94
+ algorithmType: RsaAlgorithm;
95
+ };
96
+ type BaseRsaDecryptionOptions = BaseRsaEncryptionOptions;
97
+ type BaseEciesEncryptionOptions = {
98
+ /**
99
+ * The algorithm type of the key to use for encrypting.
100
+ */
101
+ algorithmType: EciesAlgorithm;
102
+ /**
103
+ * The peer public key to use for ECIES.
104
+ */
105
+ peerPublicKey: string;
106
+ };
107
+ type BaseEciesDecryptionOptions = BaseEciesEncryptionOptions;
108
+ export type SignOptions = BaseAuthOptions & BaseSigningOptions;
109
+ export type VerifyOptions = BaseVerifyOptions;
110
+ export type DecryptOptions = (BaseAuthOptions & BaseEciesDecryptionOptions) | (BaseAuthOptions & BaseRsaDecryptionOptions);
111
+ export type EncryptOptions = (BaseAuthOptions & BaseEciesEncryptionOptions) | BaseRsaEncryptionOptions;
100
112
  export interface GetPublicKeyOptions {
101
113
  /**
102
114
  * The format of the public key to return.
103
115
  * @default "PEM" (Base64 of PEM-encoded SubjectPublicKeyInfo (SPKI) for P‑256)
104
116
  */
105
- format?: "PEM";
117
+ format?: "PEM" | "BASE64";
106
118
  }
107
119
  export {};
108
120
  //# sourceMappingURL=DeviceCrypto.types.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"DeviceCrypto.types.d.ts","sourceRoot":"","sources":["../src/DeviceCrypto.types.ts"],"names":[],"mappings":"AAAA,oBAAY,qBAAqB;IAC/B,kBAAkB,uBAAuB;IACzC,uBAAuB,4BAA4B;IACnD,aAAa,kBAAkB;CAChC;AAED,oBAAY,eAAe;IACzB,SAAS,cAAc;IACvB,WAAW,gBAAgB;IAC3B,WAAW,gBAAgB;CAC5B;AAED,oBAAY,UAAU;IACpB,QAAQ,aAAa;IACrB,qBAAqB,0BAA0B;CAChD;AAED,oBAAY,gBAAgB;IAC1B,sBAAsB,2BAA2B;CAClD;AAED,oBAAY,mBAAmB;IAC7B,cAAc,mBAAmB;IACjC,kBAAkB,uBAAuB;CAC1C;AAED,MAAM,WAAW,sBAAsB;IACrC;;OAEG;IACH,aAAa,EAAE,gBAAgB,GAAG,mBAAmB,CAAC;IACtD;;;;;;;OAOG;IACH,qBAAqB,CAAC,EAAE,OAAO,CAAC;IAChC;;;;;;;;;;;;;;OAcG;IACH,UAAU,CAAC,EAAE,UAAU,CAAC;IACxB;;;;OAIG;IACH,eAAe,CAAC,EAAE,OAAO,CAAC;CAC3B;AAED,UAAU,sBAAsB;IAC9B;;;;OAIG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB;;;;OAIG;IACH,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB;;;;;OAKG;IACH,UAAU,CAAC,EAAE,UAAU,CAAC;CACzB;AAED,UAAU,kBAAkB;IAC1B;;OAEG;IACH,aAAa,CAAC,EAAE,gBAAgB,CAAC;CAClC;AAED,UAAU,qBAAqB;IAC7B;;OAEG;IACH,aAAa,CAAC,EAAE,mBAAmB,CAAC;CACrC;AAED,MAAM,WAAW,WAAY,SAAQ,sBAAsB,EAAE,kBAAkB;CAAG;AAClF,MAAM,WAAW,aAAc,SAAQ,kBAAkB;CAAG;AAE5D,MAAM,WAAW,cAAe,SAAQ,sBAAsB,EAAE,qBAAqB;CAAG;AACxF,MAAM,WAAW,cAAe,SAAQ,qBAAqB;CAAG;AAEhE,MAAM,WAAW,mBAAmB;IAClC;;;OAGG;IACH,MAAM,CAAC,EAAE,KAAK,CAAC;CAChB"}
1
+ {"version":3,"file":"DeviceCrypto.types.d.ts","sourceRoot":"","sources":["../src/DeviceCrypto.types.ts"],"names":[],"mappings":"AAAA,oBAAY,qBAAqB;IAC/B,kBAAkB,uBAAuB;IACzC,uBAAuB,4BAA4B;IACnD,aAAa,kBAAkB;CAChC;AAED,oBAAY,eAAe;IACzB,SAAS,cAAc;IACvB,WAAW,gBAAgB;IAC3B,WAAW,gBAAgB;CAC5B;AAED,oBAAY,UAAU;IACpB,QAAQ,aAAa;IACrB,qBAAqB,0BAA0B;CAChD;AAED,oBAAY,gBAAgB;IAC1B,sBAAsB,2BAA2B;CAClD;AAED,KAAK,cAAc,GAAG,uBAAuB,CAAC;AAC9C,KAAK,YAAY,GAAG,gBAAgB,GAAG,oBAAoB,CAAC;AAC5D,oBAAY,mBAAmB;IAC7B,cAAc,mBAAmB;IACjC,kBAAkB,uBAAuB;IACzC,qBAAqB,0BAA0B;CAChD;AAED,MAAM,MAAM,sBAAsB,GAAG;IACnC;;OAEG;IACH,aAAa,EAAE,gBAAgB,GAAG,mBAAmB,CAAC;IACtD;;;;;;;OAOG;IACH,qBAAqB,CAAC,EAAE,OAAO,CAAC;IAChC;;;;;;;;;;;;;;OAcG;IACH,UAAU,CAAC,EAAE,UAAU,CAAC;IACxB;;;;OAIG;IACH,eAAe,CAAC,EAAE,OAAO,CAAC;CAC3B,CAAA;AAED,KAAK,eAAe,GAAG;IACrB;;;;OAIG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB;;;;OAIG;IACH,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB;;;;;OAKG;IACH,UAAU,CAAC,EAAE,UAAU,CAAC;CACzB,CAAA;AAED,KAAK,kBAAkB,GAAG;IACxB;;OAEG;IACH,aAAa,EAAE,gBAAgB,CAAC;CACjC,CAAC;AACF,KAAK,iBAAiB,GAAG,kBAAkB,CAAC;AAE5C,KAAK,wBAAwB,GAAG;IAC9B;;OAEG;IACH,aAAa,EAAE,YAAY,CAAC;CAC7B,CAAC;AACF,KAAK,wBAAwB,GAAG,wBAAwB,CAAC;AAEzD,KAAK,0BAA0B,GAAG;IAChC;;OAEG;IACH,aAAa,EAAE,cAAc,CAAC;IAC9B;;OAEG;IACH,aAAa,EAAE,MAAM,CAAC;CACvB,CAAC;AACF,KAAK,0BAA0B,GAAG,0BAA0B,CAAC;AAE7D,MAAM,MAAM,WAAW,GAAG,eAAe,GAAG,kBAAkB,CAAC;AAC/D,MAAM,MAAM,aAAa,GAAG,iBAAiB,CAAC;AAE9C,MAAM,MAAM,cAAc,GACtB,CAAC,eAAe,GAAG,0BAA0B,CAAC,GAC9C,CAAC,eAAe,GAAG,wBAAwB,CAAC,CAAC;AACjD,MAAM,MAAM,cAAc,GACtB,CAAC,eAAe,GAAG,0BAA0B,CAAC,GAC9C,wBAAwB,CAAC;AAE7B,MAAM,WAAW,mBAAmB;IAClC;;;OAGG;IACH,MAAM,CAAC,EAAE,KAAK,GAAG,QAAQ,CAAC;CAC3B"}
@@ -23,5 +23,6 @@ export var EncryptionAlgorithm;
23
23
  (function (EncryptionAlgorithm) {
24
24
  EncryptionAlgorithm["RSA_2048_PKCS1"] = "RSA_2048_PKCS1";
25
25
  EncryptionAlgorithm["RSA_2048_OAEP_SHA1"] = "RSA_2048_OAEP_SHA1";
26
+ EncryptionAlgorithm["ECIES_P256_AES256_GCM"] = "ECIES_P256_AES256_GCM";
26
27
  })(EncryptionAlgorithm || (EncryptionAlgorithm = {}));
27
28
  //# sourceMappingURL=DeviceCrypto.types.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"DeviceCrypto.types.js","sourceRoot":"","sources":["../src/DeviceCrypto.types.ts"],"names":[],"mappings":"AAAA,MAAM,CAAN,IAAY,qBAIX;AAJD,WAAY,qBAAqB;IAC/B,kEAAyC,CAAA;IACzC,4EAAmD,CAAA;IACnD,wDAA+B,CAAA;AACjC,CAAC,EAJW,qBAAqB,KAArB,qBAAqB,QAIhC;AAED,MAAM,CAAN,IAAY,eAIX;AAJD,WAAY,eAAe;IACzB,0CAAuB,CAAA;IACvB,8CAA2B,CAAA;IAC3B,8CAA2B,CAAA;AAC7B,CAAC,EAJW,eAAe,KAAf,eAAe,QAI1B;AAED,MAAM,CAAN,IAAY,UAGX;AAHD,WAAY,UAAU;IACpB,mCAAqB,CAAA;IACrB,6DAA+C,CAAA;AACjD,CAAC,EAHW,UAAU,KAAV,UAAU,QAGrB;AAED,MAAM,CAAN,IAAY,gBAEX;AAFD,WAAY,gBAAgB;IAC1B,qEAAiD,CAAA;AACnD,CAAC,EAFW,gBAAgB,KAAhB,gBAAgB,QAE3B;AAED,MAAM,CAAN,IAAY,mBAGX;AAHD,WAAY,mBAAmB;IAC7B,wDAAiC,CAAA;IACjC,gEAAyC,CAAA;AAC3C,CAAC,EAHW,mBAAmB,KAAnB,mBAAmB,QAG9B","sourcesContent":["export enum GenerateKeyPairResult {\n KEY_PAIR_GENERATED = \"KEY_PAIR_GENERATED\",\n KEY_PAIR_ALREADY_EXISTS = \"KEY_PAIR_ALREADY_EXISTS\",\n NOT_AVAILABLE = \"NOT_AVAILABLE\",\n}\n\nexport enum AuthCheckResult {\n AVAILABLE = \"AVAILABLE\",\n NO_HARDWARE = \"NO_HARDWARE\",\n UNAVAILABLE = \"UNAVAILABLE\",\n}\n\nexport enum AuthMethod {\n PASSCODE = \"PASSCODE\",\n PASSCODE_OR_BIOMETRIC = \"PASSCODE_OR_BIOMETRIC\",\n}\n\nexport enum SigningAlgorithm {\n ECDSA_SECP256R1_SHA256 = \"ECDSA_SECP256R1_SHA256\",\n}\n\nexport enum EncryptionAlgorithm {\n RSA_2048_PKCS1 = \"RSA_2048_PKCS1\",\n RSA_2048_OAEP_SHA1 = \"RSA_2048_OAEP_SHA1\",\n}\n\nexport interface GenerateKeyPairOptions {\n /**\n * The algorithm type of the key to generate.\n */\n algorithmType: SigningAlgorithm | EncryptionAlgorithm;\n /**\n * Whether to require authentication to sign.\n * Setting this to true will prompt biometric or passcode authentication before signing.\n *\n * Please check the *`isAuthCheckAvailable`* function to see if authentication is available.\n * If authentication is not available, generating a key pair will throw an error.\n * @default false\n */\n requireAuthentication?: boolean;\n /**\n * The method of authentication to use.\n * Note that on Android you have to define it when signing.\n *\n * If you want to allow to use Face ID, you need to add the following to your app.json config file:\n * ```json\n * \"ios\": {\n * \"infoPlist\": {\n * \"NSFaceIDUsageDescription\": \"We use Face ID to secure your data.\",\n * }\n * }\n * ```\n * @default AuthMethod.PASSCODE_OR_BIOMETRIC\n * @platform ios 🍏\n */\n authMethod?: AuthMethod;\n /**\n * If the Strong Box is available, it will be used instead of the Trusted Execution Environment (TEE).\n * @default false\n * @platform android 🤖\n */\n preferStrongBox?: boolean;\n}\n\ninterface BaseSignDecryptOptions {\n /**\n * The title of the prompt to show when authentication is required.\n * @default \"Unlock\"\n * @platform android 🤖\n */\n promptTitle?: string;\n /**\n * The subtitle of the prompt to show when authentication is required.\n * @default \"Enter your PIN to continue\"\n * @platform android 🤖\n */\n promptSubtitle?: string;\n /**\n * The method of authentication to use.\n * Note that on iOS you have to define it when generating the key pair.\n * @default AuthMethod.PASSCODE_OR_BIOMETRIC\n * @platform android 🤖\n */\n authMethod?: AuthMethod;\n}\n\ninterface BaseSigningOptions {\n /**\n * The algorithm type of the key to use for signing.\n */\n algorithmType?: SigningAlgorithm;\n}\n\ninterface BaseEncryptionOptions {\n /**\n * The algorithm type of the key to use for encrypting.\n */\n algorithmType?: EncryptionAlgorithm;\n}\n\nexport interface SignOptions extends BaseSignDecryptOptions, BaseSigningOptions {}\nexport interface VerifyOptions extends BaseSigningOptions {}\n\nexport interface DecryptOptions extends BaseSignDecryptOptions, BaseEncryptionOptions {}\nexport interface EncryptOptions extends BaseEncryptionOptions {}\n\nexport interface GetPublicKeyOptions {\n /**\n * The format of the public key to return.\n * @default \"PEM\" (Base64 of PEM-encoded SubjectPublicKeyInfo (SPKI) for P‑256)\n */\n format?: \"PEM\";\n}\n"]}
1
+ {"version":3,"file":"DeviceCrypto.types.js","sourceRoot":"","sources":["../src/DeviceCrypto.types.ts"],"names":[],"mappings":"AAAA,MAAM,CAAN,IAAY,qBAIX;AAJD,WAAY,qBAAqB;IAC/B,kEAAyC,CAAA;IACzC,4EAAmD,CAAA;IACnD,wDAA+B,CAAA;AACjC,CAAC,EAJW,qBAAqB,KAArB,qBAAqB,QAIhC;AAED,MAAM,CAAN,IAAY,eAIX;AAJD,WAAY,eAAe;IACzB,0CAAuB,CAAA;IACvB,8CAA2B,CAAA;IAC3B,8CAA2B,CAAA;AAC7B,CAAC,EAJW,eAAe,KAAf,eAAe,QAI1B;AAED,MAAM,CAAN,IAAY,UAGX;AAHD,WAAY,UAAU;IACpB,mCAAqB,CAAA;IACrB,6DAA+C,CAAA;AACjD,CAAC,EAHW,UAAU,KAAV,UAAU,QAGrB;AAED,MAAM,CAAN,IAAY,gBAEX;AAFD,WAAY,gBAAgB;IAC1B,qEAAiD,CAAA;AACnD,CAAC,EAFW,gBAAgB,KAAhB,gBAAgB,QAE3B;AAID,MAAM,CAAN,IAAY,mBAIX;AAJD,WAAY,mBAAmB;IAC7B,wDAAiC,CAAA;IACjC,gEAAyC,CAAA;IACzC,sEAA+C,CAAA;AACjD,CAAC,EAJW,mBAAmB,KAAnB,mBAAmB,QAI9B","sourcesContent":["export enum GenerateKeyPairResult {\n KEY_PAIR_GENERATED = \"KEY_PAIR_GENERATED\",\n KEY_PAIR_ALREADY_EXISTS = \"KEY_PAIR_ALREADY_EXISTS\",\n NOT_AVAILABLE = \"NOT_AVAILABLE\",\n}\n\nexport enum AuthCheckResult {\n AVAILABLE = \"AVAILABLE\",\n NO_HARDWARE = \"NO_HARDWARE\",\n UNAVAILABLE = \"UNAVAILABLE\",\n}\n\nexport enum AuthMethod {\n PASSCODE = \"PASSCODE\",\n PASSCODE_OR_BIOMETRIC = \"PASSCODE_OR_BIOMETRIC\",\n}\n\nexport enum SigningAlgorithm {\n ECDSA_SECP256R1_SHA256 = \"ECDSA_SECP256R1_SHA256\",\n}\n\ntype EciesAlgorithm = \"ECIES_P256_AES256_GCM\";\ntype RsaAlgorithm = \"RSA_2048_PKCS1\" | \"RSA_2048_OAEP_SHA1\";\nexport enum EncryptionAlgorithm {\n RSA_2048_PKCS1 = \"RSA_2048_PKCS1\",\n RSA_2048_OAEP_SHA1 = \"RSA_2048_OAEP_SHA1\",\n ECIES_P256_AES256_GCM = \"ECIES_P256_AES256_GCM\",\n}\n\nexport type GenerateKeyPairOptions = {\n /**\n * The algorithm type of the key to generate.\n */\n algorithmType: SigningAlgorithm | EncryptionAlgorithm;\n /**\n * Whether to require authentication to sign.\n * Setting this to true will prompt biometric or passcode authentication before signing.\n *\n * Please check the *`isAuthCheckAvailable`* function to see if authentication is available.\n * If authentication is not available, generating a key pair will throw an error.\n * @default false\n */\n requireAuthentication?: boolean;\n /**\n * The method of authentication to use.\n * Note that on Android you have to define it when signing.\n *\n * If you want to allow to use Face ID, you need to add the following to your app.json config file:\n * ```json\n * \"ios\": {\n * \"infoPlist\": {\n * \"NSFaceIDUsageDescription\": \"We use Face ID to secure your data.\",\n * }\n * }\n * ```\n * @default AuthMethod.PASSCODE_OR_BIOMETRIC\n * @platform ios 🍏\n */\n authMethod?: AuthMethod;\n /**\n * If the Strong Box is available, it will be used instead of the Trusted Execution Environment (TEE).\n * @default false\n * @platform android 🤖\n */\n preferStrongBox?: boolean;\n}\n\ntype BaseAuthOptions = {\n /**\n * The title of the prompt to show when authentication is required.\n * @default \"Unlock\"\n * @platform android 🤖\n */\n promptTitle?: string;\n /**\n * The subtitle of the prompt to show when authentication is required.\n * @default \"Enter your PIN to continue\"\n * @platform android 🤖\n */\n promptSubtitle?: string;\n /**\n * The method of authentication to use.\n * Note that on iOS you have to define it when generating the key pair.\n * @default AuthMethod.PASSCODE_OR_BIOMETRIC\n * @platform android 🤖\n */\n authMethod?: AuthMethod;\n}\n\ntype BaseSigningOptions = {\n /**\n * The algorithm type of the key to use for signing.\n */\n algorithmType: SigningAlgorithm;\n};\ntype BaseVerifyOptions = BaseSigningOptions;\n\ntype BaseRsaEncryptionOptions = {\n /**\n * The algorithm type of the key to use for encrypting.\n */\n algorithmType: RsaAlgorithm;\n};\ntype BaseRsaDecryptionOptions = BaseRsaEncryptionOptions;\n\ntype BaseEciesEncryptionOptions = {\n /**\n * The algorithm type of the key to use for encrypting.\n */\n algorithmType: EciesAlgorithm;\n /**\n * The peer public key to use for ECIES.\n */\n peerPublicKey: string;\n};\ntype BaseEciesDecryptionOptions = BaseEciesEncryptionOptions;\n\nexport type SignOptions = BaseAuthOptions & BaseSigningOptions;\nexport type VerifyOptions = BaseVerifyOptions;\n\nexport type DecryptOptions =\n | (BaseAuthOptions & BaseEciesDecryptionOptions)\n | (BaseAuthOptions & BaseRsaDecryptionOptions);\nexport type EncryptOptions =\n | (BaseAuthOptions & BaseEciesEncryptionOptions)\n | BaseRsaEncryptionOptions;\n\nexport interface GetPublicKeyOptions {\n /**\n * The format of the public key to return.\n * @default \"PEM\" (Base64 of PEM-encoded SubjectPublicKeyInfo (SPKI) for P‑256)\n */\n format?: \"PEM\" | \"BASE64\";\n}\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"DeviceCryptoModule.d.ts","sourceRoot":"","sources":["../src/DeviceCryptoModule.ts"],"names":[],"mappings":"AAEA,OAAO,EACL,eAAe,EACf,sBAAsB,EACtB,qBAAqB,EACrB,mBAAmB,EAEnB,WAAW,EACX,cAAc,EACd,aAAa,EAEb,cAAc,EAEf,MAAM,sBAAsB,CAAC;;IA8B5B;;;OAGG;gCAvBqB,eAAe;IAyBvC;;;;OAIG;gCA5BqB,OAAO;IA8B/B;;;;OAIG;6BAC4B,MAAM,YAAY,sBAAsB;IASvE;;;;;OAKG;0BACmB,MAAM,YAAY,mBAAmB;IAQ3D;;;;OAIG;2BA3DkB,MAAM,KAAG,OAAO;IA6DrC;;;OAGG;mBA/DQ,MAAM,EAAE;IAiEnB;;;;;;OAMG;kBACiB,MAAM,QAAQ,MAAM,YAAY,WAAW;IAS/D;;;;;;;OAOG;oBAEM,MAAM,QACP,MAAM,aACD,MAAM,YACP,aAAa;IAOzB;;;;;;;;;OASG;qBACoB,MAAM,QAAQ,MAAM,YAAY,cAAc;IAMrE;;;;;;OAMG;qBACoB,MAAM,QAAQ,MAAM,YAAY,cAAc;;AA7GvE,wBAsHE"}
1
+ {"version":3,"file":"DeviceCryptoModule.d.ts","sourceRoot":"","sources":["../src/DeviceCryptoModule.ts"],"names":[],"mappings":"AAEA,OAAO,EACL,eAAe,EACf,sBAAsB,EACtB,qBAAqB,EACrB,mBAAmB,EAEnB,WAAW,EACX,cAAc,EACd,aAAa,EAEb,cAAc,EAEf,MAAM,sBAAsB,CAAC;;IA8B5B;;;OAGG;gCAvBqB,eAAe;IAyBvC;;;;OAIG;gCA5BqB,OAAO;IA8B/B;;;;OAIG;6BAC4B,MAAM,YAAY,sBAAsB;IASvE;;;;;OAKG;0BACmB,MAAM,YAAY,mBAAmB;IAW3D;;;;OAIG;2BA9DkB,MAAM,KAAG,OAAO;IAgErC;;;OAGG;mBAlEQ,MAAM,EAAE;IAoEnB;;;;;;OAMG;kBACiB,MAAM,QAAQ,MAAM,YAAY,WAAW;IAU/D;;;;;;;OAOG;oBAEM,MAAM,QACP,MAAM,aACD,MAAM,YACP,aAAa;IAQzB;;;;;;;;;OASG;qBACoB,MAAM,QAAQ,MAAM,YAAY,cAAc;IAcrE;;;;;;OAMG;qBACoB,MAAM,QAAQ,MAAM,YAAY,cAAc;;AA1HvE,wBAqIE"}
@@ -45,6 +45,9 @@ export default {
45
45
  if (options?.format === "PEM") {
46
46
  return base64ToPem(publicKey, "PUBLIC KEY");
47
47
  }
48
+ else if (options?.format === "BASE64") {
49
+ return publicKey;
50
+ }
48
51
  return base64ToPem(publicKey, "PUBLIC KEY");
49
52
  },
50
53
  /**
@@ -101,6 +104,14 @@ export default {
101
104
  encrypt: async (alias, data, options) => {
102
105
  const o = {
103
106
  algoType: options?.algorithmType ?? EncryptionAlgorithm.RSA_2048_PKCS1,
107
+ // @ts-ignore
108
+ title: options?.promptTitle ?? "Unlock",
109
+ // @ts-ignore
110
+ subtitle: options?.promptSubtitle ?? "Enter your PIN to continue",
111
+ // @ts-ignore
112
+ authMethod: options?.authMethod ?? AuthMethod.PASSCODE_OR_BIOMETRIC,
113
+ // @ts-ignore
114
+ peerPublicKey: options?.peerPublicKey ?? "",
104
115
  };
105
116
  return module.encrypt(alias, data, o);
106
117
  },
@@ -117,6 +128,8 @@ export default {
117
128
  subtitle: options?.promptSubtitle ?? "Enter your PIN to continue",
118
129
  authMethod: options?.authMethod ?? AuthMethod.PASSCODE_OR_BIOMETRIC,
119
130
  algoType: options?.algorithmType ?? EncryptionAlgorithm.RSA_2048_PKCS1,
131
+ // @ts-ignore
132
+ peerPublicKey: options?.peerPublicKey ?? "",
120
133
  };
121
134
  return module.decrypt(alias, data, o);
122
135
  },
@@ -1 +1 @@
1
- {"version":3,"file":"DeviceCryptoModule.js","sourceRoot":"","sources":["../src/DeviceCryptoModule.ts"],"names":[],"mappings":"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","sourcesContent":["import { NativeModule, requireNativeModule } from \"expo\";\n\nimport {\n AuthCheckResult,\n GenerateKeyPairOptions,\n GenerateKeyPairResult,\n GetPublicKeyOptions,\n AuthMethod,\n SignOptions,\n DecryptOptions,\n VerifyOptions,\n SigningAlgorithm,\n EncryptOptions,\n EncryptionAlgorithm,\n} from \"./DeviceCrypto.types\";\n\nfunction base64ToPem(base64: string, label = \"DATA\") {\n const normalized = base64.replace(/\\s+/g, \"\");\n const wrapped = normalized.match(/.{1,64}/g)?.join(\"\\n\") ?? \"\";\n\n return `-----BEGIN ${label}-----\\n${wrapped}\\n-----END ${label}-----\\n`;\n}\n\ndeclare class DeviceCryptoModule extends NativeModule {\n isAuthCheckAvailable(): AuthCheckResult;\n isStrongBoxAvailable(): boolean;\n generateKeyPair(alias: string, o: any): GenerateKeyPairResult;\n getPublicKey(alias: string): string | null;\n removeKeyPair(alias: string): boolean;\n aliases(): string[];\n sign(alias: string, data: string, o: any): Promise<string | null>;\n verify(\n alias: string,\n data: string,\n signature: string,\n o: any\n ): boolean | null;\n encrypt(alias: string, data: string, o: any): Promise<string | null>;\n decrypt(alias: string, data: string, o: any): Promise<string | null>;\n}\n\nconst module = requireNativeModule<DeviceCryptoModule>(\"DeviceCrypto\");\n\nexport default {\n /**\n * Checks if the biometric or passcode authentication is available.\n * @returns The result of the operation.\n */\n isAuthCheckAvailable: module.isAuthCheckAvailable,\n /**\n * Checks if the strong box is available .\n * @returns The result of the operation.\n * @platform android 🤖\n */\n isStrongBoxAvailable: module.isStrongBoxAvailable,\n /**\n * Creates a new ECDSA P‑256 key pair for the given alias, if it doesn’t already exist.\n * @param alias - The alias to use for the key pair.\n * @returns The result of the operation.\n */\n generateKeyPair: async (alias: string, options?: GenerateKeyPairOptions) => {\n const o = {\n algoType: options?.algorithmType,\n reqAuth: options?.requireAuthentication ?? false,\n authMethod: options?.authMethod ?? AuthMethod.PASSCODE_OR_BIOMETRIC,\n preferStrongBox: options?.preferStrongBox ?? false,\n };\n return module.generateKeyPair(alias, o);\n },\n /**\n * Returns the public key for the given alias.\n * @param alias - The alias to use for the key pair.\n * @param options - The options for the operation.\n * @returns The public key.\n */\n getPublicKey: (alias: string, options?: GetPublicKeyOptions) => {\n const publicKey = module.getPublicKey(alias);\n if (!publicKey) return null;\n if (options?.format === \"PEM\") {\n return base64ToPem(publicKey, \"PUBLIC KEY\");\n }\n return base64ToPem(publicKey, \"PUBLIC KEY\");\n },\n /**\n * Removes the key pair for the given alias.\n * @param alias - The alias to use for the key pair.\n * @returns True if the key pair was removed, false otherwise.\n */\n removeKeyPair: module.removeKeyPair,\n /**\n * Lists the aliases for the key pairs.\n * @returns The aliases.\n */\n aliases: module.aliases,\n /**\n * Signs the given data with the private key for the given alias.\n * @param alias - The alias to use for the key pair.\n * @param data - The data to sign.\n * @param options - The options for the operation.\n * @returns The signature.\n */\n sign: async (alias: string, data: string, options?: SignOptions) => {\n const o = {\n title: options?.promptTitle ?? \"Unlock\",\n subtitle: options?.promptSubtitle ?? \"Enter your PIN to continue\",\n authMethod: options?.authMethod ?? AuthMethod.PASSCODE_OR_BIOMETRIC,\n algoType: options?.algorithmType ?? SigningAlgorithm.ECDSA_SECP256R1_SHA256,\n };\n return module.sign(alias, data, o);\n },\n /**\n * Verifies the given data with the signature for the given alias.\n * @param alias - The alias to use for the key pair.\n * @param data - The data to verify in UTF-8 format.\n * @param signature - The signature to verify in Base64 format.\n * @param options - The options for the operation.\n * @returns True if the signature is valid, false otherwise.\n */\n verify: async (\n alias: string,\n data: string,\n signature: string,\n options?: VerifyOptions\n ) => {\n const o = {\n algoType: options?.algorithmType ?? SigningAlgorithm.ECDSA_SECP256R1_SHA256,\n };\n return module.verify(alias, data, signature, o);\n },\n /**\n * Signs the given data with the private key for the given alias.\n * @param alias - The alias to use for the key pair.\n * @param data - The data to sign in UTF-8 format.\n * @param options - The options for the operation.\n * @default { \n * algoType: EncryptionAlgorithm.RSA_2048_PKCS1, \n * }\n * @returns The signature.\n */\n encrypt: async (alias: string, data: string, options?: EncryptOptions) => {\n const o = {\n algoType: options?.algorithmType ?? EncryptionAlgorithm.RSA_2048_PKCS1,\n };\n return module.encrypt(alias, data, o);\n },\n /**\n * Decrypts the given data with the private key for the given alias.\n * @param alias - The alias to use for the key pair.\n * @param data - The data to decrypt in Base64 format.\n * @param options - The options for the operation.\n * @returns The decrypted data.\n */\n decrypt: async (alias: string, data: string, options?: DecryptOptions) => {\n const o = {\n title: options?.promptTitle ?? \"Unlock\",\n subtitle: options?.promptSubtitle ?? \"Enter your PIN to continue\",\n authMethod: options?.authMethod ?? AuthMethod.PASSCODE_OR_BIOMETRIC,\n algoType: options?.algorithmType ?? EncryptionAlgorithm.RSA_2048_PKCS1,\n };\n return module.decrypt(alias, data, o);\n },\n};"]}
1
+ {"version":3,"file":"DeviceCryptoModule.js","sourceRoot":"","sources":["../src/DeviceCryptoModule.ts"],"names":[],"mappings":"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","sourcesContent":["import { NativeModule, requireNativeModule } from \"expo\";\n\nimport {\n AuthCheckResult,\n GenerateKeyPairOptions,\n GenerateKeyPairResult,\n GetPublicKeyOptions,\n AuthMethod,\n SignOptions,\n DecryptOptions,\n VerifyOptions,\n SigningAlgorithm,\n EncryptOptions,\n EncryptionAlgorithm,\n} from \"./DeviceCrypto.types\";\n\nfunction base64ToPem(base64: string, label = \"DATA\") {\n const normalized = base64.replace(/\\s+/g, \"\");\n const wrapped = normalized.match(/.{1,64}/g)?.join(\"\\n\") ?? \"\";\n\n return `-----BEGIN ${label}-----\\n${wrapped}\\n-----END ${label}-----\\n`;\n}\n\ndeclare class DeviceCryptoModule extends NativeModule {\n isAuthCheckAvailable(): AuthCheckResult;\n isStrongBoxAvailable(): boolean;\n generateKeyPair(alias: string, o: any): GenerateKeyPairResult;\n getPublicKey(alias: string): string | null;\n removeKeyPair(alias: string): boolean;\n aliases(): string[];\n sign(alias: string, data: string, o: any): Promise<string | null>;\n verify(\n alias: string,\n data: string,\n signature: string,\n o: any\n ): boolean | null;\n encrypt(alias: string, data: string, o: any): Promise<string | null>;\n decrypt(alias: string, data: string, o: any): Promise<string | null>;\n}\n\nconst module = requireNativeModule<DeviceCryptoModule>(\"DeviceCrypto\");\n\nexport default {\n /**\n * Checks if the biometric or passcode authentication is available.\n * @returns The result of the operation.\n */\n isAuthCheckAvailable: module.isAuthCheckAvailable,\n /**\n * Checks if the strong box is available .\n * @returns The result of the operation.\n * @platform android 🤖\n */\n isStrongBoxAvailable: module.isStrongBoxAvailable,\n /**\n * Creates a new ECDSA P‑256 key pair for the given alias, if it doesn’t already exist.\n * @param alias - The alias to use for the key pair.\n * @returns The result of the operation.\n */\n generateKeyPair: async (alias: string, options?: GenerateKeyPairOptions) => {\n const o = {\n algoType: options?.algorithmType,\n reqAuth: options?.requireAuthentication ?? false,\n authMethod: options?.authMethod ?? AuthMethod.PASSCODE_OR_BIOMETRIC,\n preferStrongBox: options?.preferStrongBox ?? false,\n };\n return module.generateKeyPair(alias, o);\n },\n /**\n * Returns the public key for the given alias.\n * @param alias - The alias to use for the key pair.\n * @param options - The options for the operation.\n * @returns The public key.\n */\n getPublicKey: (alias: string, options?: GetPublicKeyOptions) => {\n const publicKey = module.getPublicKey(alias);\n if (!publicKey) return null;\n if (options?.format === \"PEM\") {\n return base64ToPem(publicKey, \"PUBLIC KEY\");\n } else if (options?.format === \"BASE64\") {\n return publicKey;\n }\n\n return base64ToPem(publicKey, \"PUBLIC KEY\");\n },\n /**\n * Removes the key pair for the given alias.\n * @param alias - The alias to use for the key pair.\n * @returns True if the key pair was removed, false otherwise.\n */\n removeKeyPair: module.removeKeyPair,\n /**\n * Lists the aliases for the key pairs.\n * @returns The aliases.\n */\n aliases: module.aliases,\n /**\n * Signs the given data with the private key for the given alias.\n * @param alias - The alias to use for the key pair.\n * @param data - The data to sign.\n * @param options - The options for the operation.\n * @returns The signature.\n */\n sign: async (alias: string, data: string, options?: SignOptions) => {\n const o = {\n title: options?.promptTitle ?? \"Unlock\",\n subtitle: options?.promptSubtitle ?? \"Enter your PIN to continue\",\n authMethod: options?.authMethod ?? AuthMethod.PASSCODE_OR_BIOMETRIC,\n algoType:\n options?.algorithmType ?? SigningAlgorithm.ECDSA_SECP256R1_SHA256,\n };\n return module.sign(alias, data, o);\n },\n /**\n * Verifies the given data with the signature for the given alias.\n * @param alias - The alias to use for the key pair.\n * @param data - The data to verify in UTF-8 format.\n * @param signature - The signature to verify in Base64 format.\n * @param options - The options for the operation.\n * @returns True if the signature is valid, false otherwise.\n */\n verify: async (\n alias: string,\n data: string,\n signature: string,\n options?: VerifyOptions\n ) => {\n const o = {\n algoType:\n options?.algorithmType ?? SigningAlgorithm.ECDSA_SECP256R1_SHA256,\n };\n return module.verify(alias, data, signature, o);\n },\n /**\n * Signs the given data with the private key for the given alias.\n * @param alias - The alias to use for the key pair.\n * @param data - The data to sign in UTF-8 format.\n * @param options - The options for the operation.\n * @default {\n * algoType: EncryptionAlgorithm.RSA_2048_PKCS1,\n * }\n * @returns The signature.\n */\n encrypt: async (alias: string, data: string, options?: EncryptOptions) => {\n const o = {\n algoType: options?.algorithmType ?? EncryptionAlgorithm.RSA_2048_PKCS1,\n // @ts-ignore\n title: options?.promptTitle ?? \"Unlock\",\n // @ts-ignore\n subtitle: options?.promptSubtitle ?? \"Enter your PIN to continue\",\n // @ts-ignore\n authMethod: options?.authMethod ?? AuthMethod.PASSCODE_OR_BIOMETRIC,\n // @ts-ignore\n peerPublicKey: options?.peerPublicKey ?? \"\",\n };\n return module.encrypt(alias, data, o);\n },\n /**\n * Decrypts the given data with the private key for the given alias.\n * @param alias - The alias to use for the key pair.\n * @param data - The data to decrypt in Base64 format.\n * @param options - The options for the operation.\n * @returns The decrypted data.\n */\n decrypt: async (alias: string, data: string, options?: DecryptOptions) => {\n const o = {\n title: options?.promptTitle ?? \"Unlock\",\n subtitle: options?.promptSubtitle ?? \"Enter your PIN to continue\",\n authMethod: options?.authMethod ?? AuthMethod.PASSCODE_OR_BIOMETRIC,\n algoType: options?.algorithmType ?? EncryptionAlgorithm.RSA_2048_PKCS1,\n // @ts-ignore\n peerPublicKey: options?.peerPublicKey ?? \"\",\n };\n return module.decrypt(alias, data, o);\n },\n};\n"]}
@@ -1,6 +1,6 @@
1
1
  Pod::Spec.new do |s|
2
2
  s.name = 'DeviceCrypto'
3
- s.version = '0.2.3'
3
+ s.version = '0.3.0'
4
4
  s.summary = 'Device crypto'
5
5
  s.description = 'Device crypto for Expo using device hardware capabilities'
6
6
  s.author = 'Piotr Pietras'
@@ -2,6 +2,7 @@ import ExpoModulesCore
2
2
  import Security
3
3
  import Foundation
4
4
  import LocalAuthentication
5
+ import CryptoKit
5
6
 
6
7
  enum SecureSigningModuleResult: String {
7
8
  case KEY_PAIR_GENERATED = "KEY_PAIR_GENERATED"
@@ -24,6 +25,7 @@ enum AlgorithmType: String {
24
25
  case ECDSA_SECP256R1_SHA256 = "ECDSA_SECP256R1_SHA256"
25
26
  case RSA_2048_PKCS1 = "RSA_2048_PKCS1"
26
27
  case RSA_2048_OAEP_SHA1 = "RSA_2048_OAEP_SHA1"
28
+ case ECIES_P256_AES256_GCM = "ECIES_P256_AES256_GCM"
27
29
  }
28
30
 
29
31
  public class DeviceCryptoModule: Module {
@@ -35,6 +37,8 @@ public class DeviceCryptoModule: Module {
35
37
  return .rsaEncryptionPKCS1
36
38
  case .RSA_2048_OAEP_SHA1:
37
39
  return .rsaEncryptionOAEPSHA1
40
+ case .ECIES_P256_AES256_GCM:
41
+ return .eciesEncryptionCofactorX963SHA256AESGCM
38
42
  }
39
43
  }
40
44
 
@@ -103,6 +107,20 @@ public class DeviceCryptoModule: Module {
103
107
  return Data([0x80 | UInt8(bytes.count)]) + Data(bytes)
104
108
  }
105
109
 
110
+ // Converts P‑256 SPKI DER format to ANSI x962 EC point format.
111
+ private func spkiP256ToX962(_ spki: Data) -> Data? {
112
+ let prefix = Data([
113
+ 0x30, 0x59,
114
+ 0x30, 0x13,
115
+ 0x06, 0x07, 0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x02, 0x01,
116
+ 0x06, 0x08, 0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x03, 0x01, 0x07,
117
+ 0x03, 0x42, 0x00
118
+ ])
119
+ guard spki.count == prefix.count + 65 else { return nil }
120
+ guard spki.starts(with: prefix) else { return nil }
121
+ return spki.suffix(65)
122
+ }
123
+
106
124
  private func getSecKeyQuery(_ alias: String, keyClass: CFString, returnRef: Bool = true) -> [String: Any] {
107
125
  var query: [String: Any] = [
108
126
  kSecClass as String: kSecClassKey,
@@ -198,6 +216,76 @@ public class DeviceCryptoModule: Module {
198
216
  return SecKeyCreateRandomKey(attributes, nil)
199
217
  }
200
218
 
219
+ private func buildECIES(alias: String, reqAuth: Bool, authMethod: AuthMethod) -> SecKey? {
220
+ let accessFlags: SecAccessControlCreateFlags
221
+ if reqAuth {
222
+ switch authMethod {
223
+ case .PASSCODE:
224
+ accessFlags = [.privateKeyUsage, .devicePasscode]
225
+ case .PASSCODE_OR_BIOMETRIC:
226
+ accessFlags = [.privateKeyUsage, .userPresence]
227
+ }
228
+ } else {
229
+ accessFlags = .privateKeyUsage
230
+ }
231
+
232
+ let access = SecAccessControlCreateWithFlags(
233
+ kCFAllocatorDefault,
234
+ kSecAttrAccessibleWhenUnlockedThisDeviceOnly,
235
+ accessFlags,
236
+ nil
237
+ )
238
+
239
+ let attributes: NSDictionary = [
240
+ kSecAttrKeyType: kSecAttrKeyTypeECSECPrimeRandom,
241
+ kSecAttrKeySizeInBits: 256,
242
+ kSecAttrTokenID: kSecAttrTokenIDSecureEnclave,
243
+ kSecPrivateKeyAttrs: [
244
+ kSecAttrIsPermanent: true,
245
+ kSecAttrApplicationTag: alias,
246
+ kSecAttrAccessControl: access
247
+ ],
248
+ kSecPublicKeyAttrs: [
249
+ kSecAttrIsPermanent: true,
250
+ kSecAttrApplicationTag: alias
251
+ ]
252
+ ]
253
+
254
+ return SecKeyCreateRandomKey(attributes, nil)
255
+ }
256
+
257
+ private func secKeyFromPeerPublicKey(_ peerPublicKey: String) -> SecKey? {
258
+ guard
259
+ let peerPublicKeyData = Data(base64Encoded: peerPublicKey),
260
+ let x962 = spkiP256ToX962(peerPublicKeyData)
261
+ else {
262
+ return nil
263
+ }
264
+
265
+ let attrs: [String: Any] = [
266
+ kSecAttrKeyType as String: kSecAttrKeyTypeECSECPrimeRandom,
267
+ kSecAttrKeyClass as String: kSecAttrKeyClassPublic,
268
+ kSecAttrKeySizeInBits as String: 256
269
+ ]
270
+
271
+ return SecKeyCreateWithData(x962 as CFData, attrs as CFDictionary, nil)
272
+ }
273
+
274
+ private func deriveSharedSecret(privateKey: SecKey, peerPublicKey: String) -> Data? {
275
+ guard let peerSecKey = secKeyFromPeerPublicKey(peerPublicKey) else { return nil }
276
+ let algorithm = SecKeyAlgorithm.ecdhKeyExchangeStandard
277
+ guard SecKeyIsAlgorithmSupported(privateKey, .keyExchange, algorithm) else { return nil }
278
+
279
+ let params: [String: Any] = [:]
280
+ return SecKeyCopyKeyExchangeResult(
281
+ privateKey,
282
+ algorithm,
283
+ peerSecKey,
284
+ params as CFDictionary,
285
+ nil
286
+ ) as Data?
287
+ }
288
+
201
289
  private func isAuthCheckAvailable() -> String {
202
290
  let context = LAContext()
203
291
  let available = context.canEvaluatePolicy(.deviceOwnerAuthentication, error: nil)
@@ -233,6 +321,8 @@ public class DeviceCryptoModule: Module {
233
321
  self.buildRSA(alias: alias, reqAuth: reqAuth, authMethod: authMethod!)
234
322
  case .RSA_2048_OAEP_SHA1:
235
323
  self.buildRSA(alias: alias, reqAuth: reqAuth, authMethod: authMethod!)
324
+ case .ECIES_P256_AES256_GCM:
325
+ self.buildECIES(alias: alias, reqAuth: reqAuth, authMethod: authMethod!)
236
326
  default:
237
327
  throw NSError(
238
328
  domain: "DeviceCrypto",
@@ -347,12 +437,27 @@ public class DeviceCryptoModule: Module {
347
437
  return valid
348
438
  }
349
439
 
350
- private func encrypt(alias: String, data: String, o: [String: Any]) -> String? {
440
+ private func encrypt(alias: String, data: String, o: [String: Any]) throws -> String? {
441
+ let algoType = AlgorithmType(rawValue: o["algoType"] as! String)
442
+
443
+ if algoType == .ECIES_P256_AES256_GCM {
444
+ let secKey = self.getSecKeyByAlias(alias, keyClass: kSecAttrKeyClassPrivate)
445
+ guard let secKey else { return nil }
446
+ let peerPublicKey = o["peerPublicKey"] as! String
447
+ let sharedSecret = deriveSharedSecret(privateKey: secKey, peerPublicKey: peerPublicKey)
448
+ guard let sharedSecret else { return nil }
449
+
450
+ let symmetricKey = HKDF<SHA256>.deriveKey(
451
+ inputKeyMaterial: SymmetricKey(data: sharedSecret),
452
+ outputByteCount: 32
453
+ )
454
+ let sealed = try AES.GCM.seal(Data(data.utf8), using: symmetricKey)
455
+ return sealed.combined!.base64EncodedString()
456
+ }
457
+
351
458
  let secKey = self.getSecKeyByAlias(alias, keyClass: kSecAttrKeyClassPublic)
352
459
  guard let secKey else { return nil }
353
460
  let publicKey = SecKeyCopyPublicKey(secKey)!
354
-
355
- let algoType = AlgorithmType(rawValue: o["algoType"] as! String)
356
461
  let algo = self.toiOSAlgo(algorithm: algoType!)
357
462
 
358
463
  guard let encrypted = SecKeyCreateEncryptedData(
@@ -367,12 +472,27 @@ public class DeviceCryptoModule: Module {
367
472
  return encrypted.base64EncodedString()
368
473
  }
369
474
 
370
- private func decrypt(alias: String, data: String, o: [String: Any]) -> String? {
475
+ private func decrypt(alias: String, data: String, o: [String: Any]) throws -> String? {
371
476
  let secKey = self.getSecKeyByAlias(alias, keyClass: kSecAttrKeyClassPrivate)
372
477
  guard let secKey else { return nil }
373
478
  guard let encryptedData = Data(base64Encoded: data) else { return nil }
374
479
 
375
480
  let algoType = AlgorithmType(rawValue: o["algoType"] as! String)
481
+
482
+ if algoType == .ECIES_P256_AES256_GCM {
483
+ let peerPublicKey = o["peerPublicKey"] as! String
484
+ let sharedSecret = deriveSharedSecret(privateKey: secKey, peerPublicKey: peerPublicKey)
485
+ guard let sharedSecret else { return nil }
486
+
487
+ let symmetricKey = HKDF<SHA256>.deriveKey(
488
+ inputKeyMaterial: SymmetricKey(data: sharedSecret),
489
+ outputByteCount: 32
490
+ )
491
+ let sealedBox = try AES.GCM.SealedBox(combined: encryptedData)
492
+ let decrypted = try AES.GCM.open(sealedBox, using: symmetricKey)
493
+ return String(data: decrypted, encoding: .utf8)
494
+ }
495
+
376
496
  let algo = self.toiOSAlgo(algorithm: algoType!)
377
497
 
378
498
  guard let decrypted = SecKeyCreateDecryptedData(
@@ -420,11 +540,11 @@ public class DeviceCryptoModule: Module {
420
540
  }
421
541
 
422
542
  AsyncFunction("encrypt") { (alias: String, data: String, o: [String: Any]) -> String? in
423
- return self.encrypt(alias: alias, data: data, o: o)
543
+ return try self.encrypt(alias: alias, data: data, o: o)
424
544
  }
425
545
 
426
546
  AsyncFunction("decrypt") { (alias: String, data: String, o: [String: Any]) -> String? in
427
- return self.decrypt(alias: alias, data: data, o: o)
547
+ return try self.decrypt(alias: alias, data: data, o: o)
428
548
  }
429
549
  }
430
550
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "expo-device-crypto",
3
- "version": "0.2.3",
3
+ "version": "0.3.0",
4
4
  "description": "Crypto for Expo using device hardware capabilities",
5
5
  "main": "build/index.js",
6
6
  "types": "build/index.d.ts",
@@ -19,17 +19,14 @@
19
19
  "expo",
20
20
  "expo-module",
21
21
  "react-native",
22
- "native-module",
23
22
  "crypto",
24
23
  "cryptography",
25
24
  "device-crypto",
26
25
  "hardware-backed",
27
26
  "secure-enclave",
28
27
  "android-keystore",
29
- "ios-keychain",
30
- "key-management",
31
- "secure-key-storage",
32
28
  "digital-signature",
29
+ "ecies",
33
30
  "ecdsa",
34
31
  "secp256r1",
35
32
  "rsa",
@@ -19,12 +19,15 @@ export enum SigningAlgorithm {
19
19
  ECDSA_SECP256R1_SHA256 = "ECDSA_SECP256R1_SHA256",
20
20
  }
21
21
 
22
+ type EciesAlgorithm = "ECIES_P256_AES256_GCM";
23
+ type RsaAlgorithm = "RSA_2048_PKCS1" | "RSA_2048_OAEP_SHA1";
22
24
  export enum EncryptionAlgorithm {
23
25
  RSA_2048_PKCS1 = "RSA_2048_PKCS1",
24
26
  RSA_2048_OAEP_SHA1 = "RSA_2048_OAEP_SHA1",
27
+ ECIES_P256_AES256_GCM = "ECIES_P256_AES256_GCM",
25
28
  }
26
29
 
27
- export interface GenerateKeyPairOptions {
30
+ export type GenerateKeyPairOptions = {
28
31
  /**
29
32
  * The algorithm type of the key to generate.
30
33
  */
@@ -62,7 +65,7 @@ export interface GenerateKeyPairOptions {
62
65
  preferStrongBox?: boolean;
63
66
  }
64
67
 
65
- interface BaseSignDecryptOptions {
68
+ type BaseAuthOptions = {
66
69
  /**
67
70
  * The title of the prompt to show when authentication is required.
68
71
  * @default "Unlock"
@@ -84,30 +87,48 @@ interface BaseSignDecryptOptions {
84
87
  authMethod?: AuthMethod;
85
88
  }
86
89
 
87
- interface BaseSigningOptions {
90
+ type BaseSigningOptions = {
88
91
  /**
89
92
  * The algorithm type of the key to use for signing.
90
93
  */
91
- algorithmType?: SigningAlgorithm;
92
- }
94
+ algorithmType: SigningAlgorithm;
95
+ };
96
+ type BaseVerifyOptions = BaseSigningOptions;
93
97
 
94
- interface BaseEncryptionOptions {
98
+ type BaseRsaEncryptionOptions = {
95
99
  /**
96
100
  * The algorithm type of the key to use for encrypting.
97
101
  */
98
- algorithmType?: EncryptionAlgorithm;
99
- }
102
+ algorithmType: RsaAlgorithm;
103
+ };
104
+ type BaseRsaDecryptionOptions = BaseRsaEncryptionOptions;
105
+
106
+ type BaseEciesEncryptionOptions = {
107
+ /**
108
+ * The algorithm type of the key to use for encrypting.
109
+ */
110
+ algorithmType: EciesAlgorithm;
111
+ /**
112
+ * The peer public key to use for ECIES.
113
+ */
114
+ peerPublicKey: string;
115
+ };
116
+ type BaseEciesDecryptionOptions = BaseEciesEncryptionOptions;
100
117
 
101
- export interface SignOptions extends BaseSignDecryptOptions, BaseSigningOptions {}
102
- export interface VerifyOptions extends BaseSigningOptions {}
118
+ export type SignOptions = BaseAuthOptions & BaseSigningOptions;
119
+ export type VerifyOptions = BaseVerifyOptions;
103
120
 
104
- export interface DecryptOptions extends BaseSignDecryptOptions, BaseEncryptionOptions {}
105
- export interface EncryptOptions extends BaseEncryptionOptions {}
121
+ export type DecryptOptions =
122
+ | (BaseAuthOptions & BaseEciesDecryptionOptions)
123
+ | (BaseAuthOptions & BaseRsaDecryptionOptions);
124
+ export type EncryptOptions =
125
+ | (BaseAuthOptions & BaseEciesEncryptionOptions)
126
+ | BaseRsaEncryptionOptions;
106
127
 
107
128
  export interface GetPublicKeyOptions {
108
129
  /**
109
130
  * The format of the public key to return.
110
131
  * @default "PEM" (Base64 of PEM-encoded SubjectPublicKeyInfo (SPKI) for P‑256)
111
132
  */
112
- format?: "PEM";
133
+ format?: "PEM" | "BASE64";
113
134
  }
@@ -78,7 +78,10 @@ export default {
78
78
  if (!publicKey) return null;
79
79
  if (options?.format === "PEM") {
80
80
  return base64ToPem(publicKey, "PUBLIC KEY");
81
+ } else if (options?.format === "BASE64") {
82
+ return publicKey;
81
83
  }
84
+
82
85
  return base64ToPem(publicKey, "PUBLIC KEY");
83
86
  },
84
87
  /**
@@ -104,7 +107,8 @@ export default {
104
107
  title: options?.promptTitle ?? "Unlock",
105
108
  subtitle: options?.promptSubtitle ?? "Enter your PIN to continue",
106
109
  authMethod: options?.authMethod ?? AuthMethod.PASSCODE_OR_BIOMETRIC,
107
- algoType: options?.algorithmType ?? SigningAlgorithm.ECDSA_SECP256R1_SHA256,
110
+ algoType:
111
+ options?.algorithmType ?? SigningAlgorithm.ECDSA_SECP256R1_SHA256,
108
112
  };
109
113
  return module.sign(alias, data, o);
110
114
  },
@@ -123,7 +127,8 @@ export default {
123
127
  options?: VerifyOptions
124
128
  ) => {
125
129
  const o = {
126
- algoType: options?.algorithmType ?? SigningAlgorithm.ECDSA_SECP256R1_SHA256,
130
+ algoType:
131
+ options?.algorithmType ?? SigningAlgorithm.ECDSA_SECP256R1_SHA256,
127
132
  };
128
133
  return module.verify(alias, data, signature, o);
129
134
  },
@@ -132,14 +137,22 @@ export default {
132
137
  * @param alias - The alias to use for the key pair.
133
138
  * @param data - The data to sign in UTF-8 format.
134
139
  * @param options - The options for the operation.
135
- * @default {
136
- * algoType: EncryptionAlgorithm.RSA_2048_PKCS1,
140
+ * @default {
141
+ * algoType: EncryptionAlgorithm.RSA_2048_PKCS1,
137
142
  * }
138
143
  * @returns The signature.
139
144
  */
140
145
  encrypt: async (alias: string, data: string, options?: EncryptOptions) => {
141
146
  const o = {
142
147
  algoType: options?.algorithmType ?? EncryptionAlgorithm.RSA_2048_PKCS1,
148
+ // @ts-ignore
149
+ title: options?.promptTitle ?? "Unlock",
150
+ // @ts-ignore
151
+ subtitle: options?.promptSubtitle ?? "Enter your PIN to continue",
152
+ // @ts-ignore
153
+ authMethod: options?.authMethod ?? AuthMethod.PASSCODE_OR_BIOMETRIC,
154
+ // @ts-ignore
155
+ peerPublicKey: options?.peerPublicKey ?? "",
143
156
  };
144
157
  return module.encrypt(alias, data, o);
145
158
  },
@@ -156,7 +169,9 @@ export default {
156
169
  subtitle: options?.promptSubtitle ?? "Enter your PIN to continue",
157
170
  authMethod: options?.authMethod ?? AuthMethod.PASSCODE_OR_BIOMETRIC,
158
171
  algoType: options?.algorithmType ?? EncryptionAlgorithm.RSA_2048_PKCS1,
172
+ // @ts-ignore
173
+ peerPublicKey: options?.peerPublicKey ?? "",
159
174
  };
160
175
  return module.decrypt(alias, data, o);
161
176
  },
162
- };
177
+ };