expo-device-crypto 0.2.0 → 0.2.2

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
@@ -2,7 +2,7 @@
2
2
 
3
3
  > ⚠️ This module is currently in beta and is not suitable for production use.
4
4
 
5
- 🔒 Hardware-backed cryptography for Expo apps using [Android Keystore](https://developer.android.com/privacy-and-security/keystore) and Apple [Secure Enclave](https://developer.apple.com/documentation/security/protecting-keys-with-the-secure-enclave).
5
+ 🔒 Hardware-backed cryptography for Expo apps using [Android Keystore](https://developer.android.com/privacy-and-security/keystore)/[Strong Box](https://developer.android.com/privacy-and-security/keystore#StrongBoxKeyMint) and Apple [Secure Enclave](https://developer.apple.com/documentation/security/protecting-keys-with-the-secure-enclave)/[Keychain](https://developer.apple.com/documentation/security/keychain-services).
6
6
 
7
7
  ## Installation
8
8
 
@@ -48,7 +48,7 @@ If you want to allow Face ID on iOS, add this to your app config:
48
48
  **Padding:** OAEP with SHA-1 and MGF1. </br>
49
49
  **Use it for:** Encrypting small secrets with stronger padding than PKCS#1 v1.5 when interoperating with OAEP SHA-1 systems.
50
50
 
51
- More coming soon
51
+ **More coming soon...**
52
52
 
53
53
  ## Example: ECDSA with Biometric
54
54
 
@@ -176,4 +176,35 @@ const decrypted = await DeviceCrypto.decrypt(alias, encrypted ?? "", {
176
176
  - Decrypts Base64 `data` with private key.
177
177
  - Defaults: `algorithmType = RSA_2048_PKCS1`, `promptTitle = "Unlock"`, `promptSubtitle = "Enter your PIN to continue"`, `authMethod = PASSCODE_OR_BIOMETRIC`.
178
178
 
179
- > ✅ JSDoc type definitions are available in `./src/DeviceCryptoModule.ts`.
179
+ **Android only methods**
180
+
181
+ - `isStrongBoxAvailable(): boolean`
182
+ - Returns `true` if StrongBox Keystore is supported on the device.
183
+
184
+ > ✅ JSDoc type definitions are available in `./src/DeviceCryptoModule.ts`.
185
+
186
+ ## Important
187
+
188
+ ### Enable Strong Box on Android
189
+
190
+ Use `preferStrongBox: true` when generating a key pair to request [StrongBox-backed](https://developer.android.com/privacy-and-security/keystore#StrongBoxKeyMint) key storage when the device supports it.
191
+
192
+ ```ts
193
+ import DeviceCrypto, { SigningAlgorithm } from "expo-device-crypto";
194
+
195
+ const alias = "user-signing-key";
196
+
197
+ // Optional: check availability first.
198
+ const hasStrongBox = DeviceCrypto.isStrongBoxAvailable();
199
+
200
+ await DeviceCrypto.generateKeyPair(alias, {
201
+ algorithmType: SigningAlgorithm.ECDSA_SECP256R1_SHA256,
202
+ preferStrongBox: true,
203
+ });
204
+ ```
205
+
206
+ If StrongBox is not available, Android falls back to the Trusted Execution Environment (TEE).
207
+
208
+ ### RSA algorithms on iOS
209
+
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.
@@ -1,7 +1,7 @@
1
1
  apply plugin: 'com.android.library'
2
2
 
3
3
  group = 'expo.modules.devicecrypto'
4
- version = '0.2.0'
4
+ version = '0.2.2'
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.0"
38
+ versionName "0.2.2"
39
39
  }
40
40
  lintOptions {
41
41
  abortOnError false
@@ -81,21 +81,11 @@ class DeviceCryptoModule : Module() {
81
81
  }
82
82
  }
83
83
 
84
- private fun isAuthCheckAvailable(): AuthCheckResult {
85
- val biometricManager = BiometricManager.from(appContext.reactContext as Context)
86
- return when (biometricManager.canAuthenticate(BIOMETRIC_STRONG or DEVICE_CREDENTIAL)) {
87
- BiometricManager.BIOMETRIC_SUCCESS -> AuthCheckResult.AVAILABLE
88
- BiometricManager.BIOMETRIC_ERROR_NO_HARDWARE -> AuthCheckResult.NO_HARDWARE
89
- BiometricManager.BIOMETRIC_ERROR_HW_UNAVAILABLE -> AuthCheckResult.UNAVAILABLE
90
- else -> AuthCheckResult.UNAVAILABLE
91
- }
92
- }
93
-
94
84
  private fun isKeyStoreAvailable(): Boolean {
95
85
  val pm = appContext.reactContext?.packageManager ?: return false
96
86
  val appAttestKeystore = pm.hasSystemFeature(PackageManager.FEATURE_KEYSTORE_APP_ATTEST_KEY)
97
87
  val hardwareKeystore = pm.hasSystemFeature(PackageManager.FEATURE_HARDWARE_KEYSTORE)
98
- return appAttestKeystore && hardwareKeystore
88
+ return appAttestKeystore || hardwareKeystore
99
89
  }
100
90
 
101
91
  private fun isStrongBoxAvailable(): Boolean {
@@ -104,10 +94,6 @@ class DeviceCryptoModule : Module() {
104
94
  return strongboxKeystore
105
95
  }
106
96
 
107
- private fun getAliases() = KeyStore.getInstance("AndroidKeyStore").apply {
108
- load(null)
109
- }.aliases().toList()
110
-
111
97
  private fun isKeyStoreRequireAuthentication(entry: KeyStore.PrivateKeyEntry): Boolean {
112
98
  val keyFactory = KeyFactory.getInstance(entry.privateKey.algorithm, "AndroidKeyStore")
113
99
  val keyInfo = keyFactory.getKeySpec(entry.privateKey, KeyInfo::class.java)
@@ -120,27 +106,11 @@ class DeviceCryptoModule : Module() {
120
106
  }.getEntry(alias, null)
121
107
  }
122
108
 
123
- private fun retrievePublicKey(ks: KeyStore.Entry?): String? {
124
- if (ks !is KeyStore.PrivateKeyEntry) return null
125
-
126
- val publicKeyBytes: ByteArray = ks.certificate?.publicKey?.encoded ?: return null
127
- return Base64.encodeToString(publicKeyBytes, Base64.NO_WRAP)
128
- }
129
-
130
- private fun removeKeyStoreEntry(alias: String): Boolean {
131
- val ks = KeyStore.getInstance("AndroidKeyStore").apply {
132
- load(null)
133
- }
134
- if (ks.getEntry(alias, null) == null) return false
135
-
136
- ks.deleteEntry(alias)
137
- return true
138
- }
139
-
140
109
  private fun buildECDSA(
141
110
  alias: String,
142
111
  digest: String,
143
- reqAuth: Boolean): KeyPairGenerator {
112
+ reqAuth: Boolean,
113
+ strongBox: Boolean): KeyPairGenerator {
144
114
  val kpg: KeyPairGenerator = KeyPairGenerator.getInstance(
145
115
  KEY_ALGORITHM_EC,
146
116
  "AndroidKeyStore"
@@ -151,7 +121,7 @@ class DeviceCryptoModule : Module() {
151
121
  PURPOSE_SIGN or PURPOSE_VERIFY
152
122
  ).run {
153
123
  setDigests(digest)
154
- setIsStrongBoxBacked(false) // TODO: Prefer strong box if available
124
+ setIsStrongBoxBacked(strongBox)
155
125
  setUserAuthenticationRequired(reqAuth)
156
126
  if (reqAuth) {
157
127
  setUserAuthenticationParameters(
@@ -169,7 +139,8 @@ class DeviceCryptoModule : Module() {
169
139
  alias: String,
170
140
  digest: String?,
171
141
  padding: String,
172
- reqAuth: Boolean): KeyPairGenerator {
142
+ reqAuth: Boolean,
143
+ strongBox: Boolean): KeyPairGenerator {
173
144
  val kpg: KeyPairGenerator = KeyPairGenerator.getInstance(
174
145
  KEY_ALGORITHM_RSA,
175
146
  "AndroidKeyStore"
@@ -183,7 +154,7 @@ class DeviceCryptoModule : Module() {
183
154
  setDigests(digest)
184
155
  }
185
156
  setEncryptionPaddings(padding)
186
- setIsStrongBoxBacked(false) // TODO: Prefer strong box if available
157
+ setIsStrongBoxBacked(strongBox)
187
158
  setUserAuthenticationRequired(reqAuth)
188
159
  if (reqAuth) {
189
160
  setUserAuthenticationParameters(
@@ -249,191 +220,236 @@ class DeviceCryptoModule : Module() {
249
220
  }
250
221
  }
251
222
 
252
- override fun definition() = ModuleDefinition {
253
- Name("DeviceCrypto")
223
+ private fun isAuthCheckAvailable(): AuthCheckResult {
224
+ val biometricManager = BiometricManager.from(appContext.reactContext as Context)
225
+ return when (biometricManager.canAuthenticate(BIOMETRIC_STRONG or DEVICE_CREDENTIAL)) {
226
+ BiometricManager.BIOMETRIC_SUCCESS -> AuthCheckResult.AVAILABLE
227
+ BiometricManager.BIOMETRIC_ERROR_NO_HARDWARE -> AuthCheckResult.NO_HARDWARE
228
+ BiometricManager.BIOMETRIC_ERROR_HW_UNAVAILABLE -> AuthCheckResult.UNAVAILABLE
229
+ else -> AuthCheckResult.UNAVAILABLE
230
+ }
231
+ }
254
232
 
255
- Function("isAuthCheckAvailable") { ->
256
- return@Function isAuthCheckAvailable()
233
+ private fun generateKeyPair(alias: String, o: Map<String, Any?>): GenerateKeyPairResult {
234
+ val reqAuth = o["reqAuth"] as Boolean
235
+ if (reqAuth && isAuthCheckAvailable() != AuthCheckResult.AVAILABLE) {
236
+ throw Exception("NO_AUTH_AVAILABLE")
237
+ }
238
+ if (!isKeyStoreAvailable()) {
239
+ return GenerateKeyPairResult.NOT_AVAILABLE
240
+ }
241
+ if (getKeyStoreEntry(alias) != null) {
242
+ return GenerateKeyPairResult.KEY_PAIR_ALREADY_EXISTS
257
243
  }
258
244
 
259
- Function("generateKeyPair") { alias: String, o: Map<String, Any?> ->
260
- val reqAuth = o["reqAuth"] as Boolean
261
- if (reqAuth && isAuthCheckAvailable() != AuthCheckResult.AVAILABLE) {
262
- throw Exception("NO_AUTH_AVAILABLE")
263
- }
264
- if (!isKeyStoreAvailable()) {
265
- return@Function GenerateKeyPairResult.NOT_AVAILABLE
266
- }
267
- if (getKeyStoreEntry(alias) != null) {
268
- return@Function GenerateKeyPairResult.KEY_PAIR_ALREADY_EXISTS
269
- }
245
+ val algoType = AlgorithmType.valueOf(o["algoType"] as String)
246
+ val preferStrongBox = o["preferStrongBox"] as Boolean
247
+ val strongBox = preferStrongBox && isStrongBoxAvailable()
270
248
 
271
- val algoType = AlgorithmType.valueOf(o["algoType"] as String)
272
- val kpg = when (algoType) {
273
- AlgorithmType.ECDSA_SECP256R1_SHA256 -> buildECDSA(alias, DIGEST_SHA256, reqAuth)
274
- AlgorithmType.RSA_2048_PKCS1 -> buildRSA(alias, null, ENCRYPTION_PADDING_RSA_PKCS1, reqAuth)
275
- AlgorithmType.RSA_2048_OAEP_SHA1 -> buildRSA(alias, DIGEST_SHA1, ENCRYPTION_PADDING_RSA_OAEP, reqAuth)
276
- else -> throw Exception("INVALID_ALGORITHM_TYPE")
277
- }
249
+ val kpg = when (algoType) {
250
+ AlgorithmType.ECDSA_SECP256R1_SHA256 -> buildECDSA(alias, DIGEST_SHA256, reqAuth, strongBox)
251
+ AlgorithmType.RSA_2048_PKCS1 -> buildRSA(alias, null, ENCRYPTION_PADDING_RSA_PKCS1, reqAuth, strongBox)
252
+ AlgorithmType.RSA_2048_OAEP_SHA1 -> buildRSA(alias, DIGEST_SHA1, ENCRYPTION_PADDING_RSA_OAEP, reqAuth, strongBox)
253
+ else -> throw Exception("INVALID_ALGORITHM_TYPE")
254
+ }
255
+
256
+ kpg.generateKeyPair()
278
257
 
279
- kpg.generateKeyPair()
258
+ return GenerateKeyPairResult.KEY_PAIR_GENERATED
259
+ }
280
260
 
281
- return@Function GenerateKeyPairResult.KEY_PAIR_GENERATED
261
+ private fun removeKeyStoreEntry(alias: String): Boolean {
262
+ val ks = KeyStore.getInstance("AndroidKeyStore").apply {
263
+ load(null)
282
264
  }
265
+ if (ks.getEntry(alias, null) == null) return false
283
266
 
284
- Function("removeKeyPair") { alias: String ->
285
- return@Function removeKeyStoreEntry(alias)
267
+ ks.deleteEntry(alias)
268
+ return true
269
+ }
270
+
271
+ private fun getAliases() = KeyStore.getInstance("AndroidKeyStore").apply {
272
+ load(null)
273
+ }.aliases().toList()
274
+
275
+ private fun getPublicKey(alias: String): String? {
276
+ val ks = getKeyStoreEntry(alias)
277
+ if (ks !is KeyStore.PrivateKeyEntry) return null
278
+
279
+ val publicKeyBytes: ByteArray = ks.certificate?.publicKey?.encoded ?: return null
280
+ return Base64.encodeToString(publicKeyBytes, Base64.NO_WRAP)
281
+ }
282
+
283
+ private fun sign(alias: String, data: String, o: Map<String, Any?>, promise: Promise) {
284
+ val entry = getKeyStoreEntry(alias)
285
+ if (entry !is KeyStore.PrivateKeyEntry) {
286
+ promise.resolve(null)
287
+ return
286
288
  }
287
289
 
288
- Function("aliases") {
289
- return@Function getAliases()
290
+ val reqAuth = isKeyStoreRequireAuthentication(entry)
291
+
292
+ val title = o["title"] as String
293
+ val subtitle = o["subtitle"] as String
294
+ val authMethod = AuthMethod.valueOf(o["authMethod"] as String)
295
+ val algoType = AlgorithmType.valueOf(o["algoType"] as String)
296
+ val algo = toAndroidAlgo(algoType)
297
+
298
+ if (!reqAuth) {
299
+ val signature: ByteArray = Signature.getInstance(algo).apply {
300
+ initSign(entry.privateKey)
301
+ update(data.toByteArray(Charsets.UTF_8))
302
+ }.sign()
303
+ promise.resolve(Base64.encodeToString(signature, Base64.NO_WRAP))
304
+ return
305
+ } else {
306
+ showAuthPrompt(
307
+ onSuccess = {
308
+ val signature: ByteArray = Signature.getInstance(algo).apply {
309
+ initSign(entry.privateKey)
310
+ update(data.toByteArray(Charsets.UTF_8))
311
+ }.sign()
312
+ promise.resolve(Base64.encodeToString(signature, Base64.NO_WRAP))
313
+ },
314
+ onError = { error ->
315
+ promise.reject("ERR_AUTH_FAILED", error, null)
316
+ },
317
+ title = title,
318
+ subtitle = subtitle,
319
+ authMethod = authMethod
320
+ )
290
321
  }
322
+ }
291
323
 
292
- Function("getPublicKey") { alias: String ->
293
- val entry = getKeyStoreEntry(alias)
294
- return@Function retrievePublicKey(entry)
324
+ private fun verify(alias: String, data: String, signature: String, o: Map<String, Any?>): Boolean? {
325
+ val entry = getKeyStoreEntry(alias)
326
+ if (entry !is KeyStore.PrivateKeyEntry) return null
327
+
328
+ val algoType = AlgorithmType.valueOf(o["algoType"] as String)
329
+ val algo = toAndroidAlgo(algoType)
330
+
331
+ val valid: Boolean = Signature.getInstance(algo).apply {
332
+ initVerify(entry.certificate)
333
+ update(data.toByteArray(Charsets.UTF_8))
334
+ }.verify(Base64.decode(signature, Base64.NO_WRAP))
335
+ return valid
336
+ }
337
+
338
+ private fun encrypt(alias: String, data: String, o: Map<String, Any?>, promise: Promise) {
339
+ val entry = getKeyStoreEntry(alias)
340
+ if (entry !is KeyStore.PrivateKeyEntry) {
341
+ promise.resolve(null)
342
+ return
295
343
  }
296
344
 
297
- AsyncFunction("sign") { alias: String,
298
- data: String,
299
- o: Map<String, Any?>,
300
- promise: Promise ->
301
- val entry = getKeyStoreEntry(alias)
302
- if (entry !is KeyStore.PrivateKeyEntry) {
303
- promise.resolve(null)
304
- return@AsyncFunction
305
- }
345
+ val algoType = AlgorithmType.valueOf(o["algoType"] as String)
346
+ val algo = toAndroidAlgo(algoType)
347
+ val oaepSpec = getOaepSpec(algoType)
306
348
 
307
- val reqAuth = isKeyStoreRequireAuthentication(entry);
308
-
309
- val title = o["title"] as String
310
- val subtitle = o["subtitle"] as String
311
- val authMethod = AuthMethod.valueOf(o["authMethod"] as String)
312
- val algoType = AlgorithmType.valueOf(o["algoType"] as String)
313
- val algo = toAndroidAlgo(algoType)
314
-
315
- if (!reqAuth) {
316
- val signature: ByteArray = Signature.getInstance(algo).apply {
317
- initSign(entry.privateKey)
318
- update(data.toByteArray(Charsets.UTF_8))
319
- }.sign()
320
- promise.resolve(Base64.encodeToString(signature, Base64.NO_WRAP))
321
- return@AsyncFunction
322
- } else {
323
- showAuthPrompt(
324
- onSuccess = {
325
- val signature: ByteArray = Signature.getInstance(algo).apply {
326
- initSign(entry.privateKey)
327
- update(data.toByteArray(Charsets.UTF_8))
328
- }.sign()
329
- promise.resolve(Base64.encodeToString(signature, Base64.NO_WRAP))
330
- },
331
- onError = { error ->
332
- promise.reject("ERR_AUTH_FAILED", error, null)
333
- },
334
- title = title,
335
- subtitle = subtitle,
336
- authMethod = authMethod
337
- )
338
- }
349
+ val cipher = Cipher.getInstance(algo)
350
+ if (oaepSpec != null) {
351
+ cipher.init(Cipher.ENCRYPT_MODE, entry.certificate.publicKey, oaepSpec)
352
+ } else {
353
+ cipher.init(Cipher.ENCRYPT_MODE, entry.certificate.publicKey)
339
354
  }
355
+ val encrypted: ByteArray = cipher.doFinal(data.toByteArray(Charsets.UTF_8))
356
+ promise.resolve(Base64.encodeToString(encrypted, Base64.NO_WRAP))
357
+ }
340
358
 
341
- Function("verify") { alias: String,
342
- data: String,
343
- signature: String,
344
- o: Map<String, Any?> ->
345
- val entry = getKeyStoreEntry(alias)
346
- if (entry !is KeyStore.PrivateKeyEntry) return@Function null
347
-
348
- val algoType = AlgorithmType.valueOf(o["algoType"] as String)
349
- val algo = toAndroidAlgo(algoType)
350
-
351
- val valid: Boolean = Signature.getInstance(algo).apply {
352
- initVerify(entry.certificate)
353
- update(data.toByteArray(Charsets.UTF_8))
354
- }.verify(Base64.decode(signature, Base64.NO_WRAP))
355
- return@Function true
359
+ private fun decrypt(alias: String, data: String, o: Map<String, Any?>, promise: Promise) {
360
+ val entry = getKeyStoreEntry(alias)
361
+ if (entry !is KeyStore.PrivateKeyEntry) {
362
+ promise.resolve(null)
363
+ return
356
364
  }
357
365
 
366
+ val reqAuth = isKeyStoreRequireAuthentication(entry)
358
367
 
359
- AsyncFunction("encrypt") { alias: String,
360
- data: String,
361
- o: Map<String, Any?>,
362
- promise: Promise ->
363
- val entry = getKeyStoreEntry(alias)
364
- if (entry !is KeyStore.PrivateKeyEntry) {
365
- promise.resolve(null)
366
- return@AsyncFunction
367
- }
368
+ val title = o["title"] as String
369
+ val subtitle = o["subtitle"] as String
370
+ val authMethod = o["authMethod"] as String
371
+ val algoType = AlgorithmType.valueOf(o["algoType"] as String)
372
+ val algo = toAndroidAlgo(algoType)
373
+ val oaepSpec = getOaepSpec(algoType)
368
374
 
369
- val algoType = AlgorithmType.valueOf(o["algoType"] as String)
370
- val algo = toAndroidAlgo(algoType)
371
- val oaepSpec = getOaepSpec(algoType)
375
+ val cipher = Cipher.getInstance(algo)
376
+ val ciphertext = Base64.decode(data, Base64.NO_WRAP)
372
377
 
373
- val cipher = Cipher.getInstance(algo)
378
+ if (!reqAuth) {
374
379
  if (oaepSpec != null) {
375
- cipher.init(Cipher.ENCRYPT_MODE, entry.certificate.publicKey, oaepSpec)
380
+ cipher.init(Cipher.DECRYPT_MODE, entry.privateKey, oaepSpec)
376
381
  } else {
377
- cipher.init(Cipher.ENCRYPT_MODE, entry.certificate.publicKey)
382
+ cipher.init(Cipher.DECRYPT_MODE, entry.privateKey)
378
383
  }
379
- val encrypted: ByteArray = cipher.doFinal(data.toByteArray(Charsets.UTF_8))
380
- promise.resolve(Base64.encodeToString(encrypted, Base64.NO_WRAP))
384
+ val decrypted: ByteArray = cipher.doFinal(ciphertext)
385
+ promise.resolve(String(decrypted, Charsets.UTF_8))
386
+ return
387
+ } else {
388
+ showAuthPrompt(
389
+ onSuccess = {
390
+ if (oaepSpec != null) {
391
+ cipher.init(Cipher.DECRYPT_MODE, entry.privateKey, oaepSpec)
392
+ } else {
393
+ cipher.init(Cipher.DECRYPT_MODE, entry.privateKey)
394
+ }
395
+ val decrypted: ByteArray = cipher.doFinal(ciphertext)
396
+ promise.resolve(String(decrypted, Charsets.UTF_8))
397
+ },
398
+ onError = { error ->
399
+ promise.reject("ERR_AUTH_FAILED", error, null)
400
+ },
401
+ title = title,
402
+ subtitle = subtitle,
403
+ authMethod = when (authMethod) {
404
+ "PASSCODE" -> AuthMethod.PASSCODE
405
+ "PASSCODE_OR_BIOMETRIC" -> AuthMethod.PASSCODE_OR_BIOMETRIC
406
+ else -> AuthMethod.PASSCODE_OR_BIOMETRIC
407
+ }
408
+ )
381
409
  }
410
+ }
382
411
 
383
- AsyncFunction("decrypt") { alias: String,
384
- data: String,
385
- o: Map<String, Any?>,
386
- promise: Promise ->
387
- val entry = getKeyStoreEntry(alias)
388
- if (entry !is KeyStore.PrivateKeyEntry) {
389
- promise.resolve(null)
390
- return@AsyncFunction
391
- }
412
+ override fun definition() = ModuleDefinition {
413
+ Name("DeviceCrypto")
392
414
 
393
- val reqAuth = isKeyStoreRequireAuthentication(entry)
415
+ Function("isAuthCheckAvailable") { ->
416
+ return@Function isAuthCheckAvailable()
417
+ }
394
418
 
395
- val title = o["title"] as String
396
- val subtitle = o["subtitle"] as String
397
- val authMethod = o["authMethod"] as String
398
- val algoType = AlgorithmType.valueOf(o["algoType"] as String)
399
- val algo = toAndroidAlgo(algoType)
400
- val oaepSpec = getOaepSpec(algoType)
419
+ Function("isStrongBoxAvailable") { ->
420
+ return@Function isStrongBoxAvailable()
421
+ }
401
422
 
402
- val cipher = Cipher.getInstance(algo)
403
- val ciphertext = Base64.decode(data, Base64.NO_WRAP)
423
+ Function("generateKeyPair") { alias: String, o: Map<String, Any?> ->
424
+ return@Function generateKeyPair(alias, o)
425
+ }
404
426
 
405
- if (!reqAuth) {
406
- if (oaepSpec != null) {
407
- cipher.init(Cipher.DECRYPT_MODE, entry.privateKey, oaepSpec)
408
- } else {
409
- cipher.init(Cipher.DECRYPT_MODE, entry.privateKey)
410
- }
411
- val decrypted: ByteArray = cipher.doFinal(ciphertext)
412
- promise.resolve(String(decrypted, Charsets.UTF_8))
413
- return@AsyncFunction
414
- } else {
415
- showAuthPrompt(
416
- onSuccess = {
417
- if (oaepSpec != null) {
418
- cipher.init(Cipher.DECRYPT_MODE, entry.privateKey, oaepSpec)
419
- } else {
420
- cipher.init(Cipher.DECRYPT_MODE, entry.privateKey)
421
- }
422
- val decrypted: ByteArray = cipher.doFinal(ciphertext)
423
- promise.resolve(String(decrypted, Charsets.UTF_8))
424
- },
425
- onError = { error ->
426
- promise.reject("ERR_AUTH_FAILED", error, null)
427
- },
428
- title = title,
429
- subtitle = subtitle,
430
- authMethod = when (authMethod) {
431
- "PASSCODE" -> AuthMethod.PASSCODE
432
- "PASSCODE_OR_BIOMETRIC" -> AuthMethod.PASSCODE_OR_BIOMETRIC
433
- else -> AuthMethod.PASSCODE_OR_BIOMETRIC
434
- }
435
- )
436
- }
427
+ Function("removeKeyPair") { alias: String ->
428
+ return@Function removeKeyStoreEntry(alias)
429
+ }
430
+
431
+ Function("aliases") {
432
+ return@Function getAliases()
433
+ }
434
+
435
+ Function("getPublicKey") { alias: String ->
436
+ return@Function getPublicKey(alias)
437
+ }
438
+
439
+ AsyncFunction("sign") { alias: String, data: String, o: Map<String, Any?>, promise: Promise ->
440
+ return@AsyncFunction sign(alias, data, o, promise)
441
+ }
442
+
443
+ Function("verify") { alias: String, data: String, signature: String, o: Map<String, Any?> ->
444
+ return@Function verify(alias, data, signature, o)
445
+ }
446
+
447
+ AsyncFunction("encrypt") { alias: String, data: String, o: Map<String, Any?>, promise: Promise ->
448
+ return@AsyncFunction encrypt(alias, data, o, promise)
449
+ }
450
+
451
+ AsyncFunction("decrypt") { alias: String, data: String, o: Map<String, Any?>, promise: Promise ->
452
+ return@AsyncFunction decrypt(alias, data, o, promise)
437
453
  }
438
454
  }
439
455
  }
@@ -46,28 +46,34 @@ export interface GenerateKeyPairOptions {
46
46
  * }
47
47
  * ```
48
48
  * @default AuthMethod.PASSCODE_OR_BIOMETRIC
49
- * @platform ios
49
+ * @platform ios 🍏
50
50
  */
51
51
  authMethod?: AuthMethod;
52
+ /**
53
+ * If the Strong Box is available, it will be used instead of the Trusted Execution Environment (TEE).
54
+ * @default false
55
+ * @platform android 🤖
56
+ */
57
+ preferStrongBox?: boolean;
52
58
  }
53
59
  interface BaseSignDecryptOptions {
54
60
  /**
55
61
  * The title of the prompt to show when authentication is required.
56
62
  * @default "Unlock"
57
- * @platform android
63
+ * @platform android 🤖
58
64
  */
59
65
  promptTitle?: string;
60
66
  /**
61
67
  * The subtitle of the prompt to show when authentication is required.
62
68
  * @default "Enter your PIN to continue"
63
- * @platform android
69
+ * @platform android 🤖
64
70
  */
65
71
  promptSubtitle?: string;
66
72
  /**
67
73
  * The method of authentication to use.
68
74
  * Note that on iOS you have to define it when generating the key pair.
69
75
  * @default AuthMethod.PASSCODE_OR_BIOMETRIC
70
- * @platform android
76
+ * @platform android 🤖
71
77
  */
72
78
  authMethod?: AuthMethod;
73
79
  }
@@ -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;CACzB;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,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 +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\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;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"]}
@@ -5,6 +5,12 @@ declare const _default: {
5
5
  * @returns The result of the operation.
6
6
  */
7
7
  isAuthCheckAvailable: () => AuthCheckResult;
8
+ /**
9
+ * Checks if the strong box is available .
10
+ * @returns The result of the operation.
11
+ * @platform android 🤖
12
+ */
13
+ isStrongBoxAvailable: () => boolean;
8
14
  /**
9
15
  * Creates a new ECDSA P‑256 key pair for the given alias, if it doesn’t already exist.
10
16
  * @param alias - The alias to use for the key pair.
@@ -34,12 +40,6 @@ declare const _default: {
34
40
  * @param alias - The alias to use for the key pair.
35
41
  * @param data - The data to sign.
36
42
  * @param options - The options for the operation.
37
- * @default {
38
- * algoType: SigningAlgorithm.ECDSA_SHA256,
39
- * promptTitle: "Unlock",
40
- * promptSubtitle: "Enter your PIN to continue",
41
- * authMethod: AuthMethod.PASSCODE_OR_BIOMETRIC
42
- * }
43
43
  * @returns The signature.
44
44
  */
45
45
  sign: (alias: string, data: string, options?: SignOptions) => Promise<string | null>;
@@ -49,9 +49,6 @@ declare const _default: {
49
49
  * @param data - The data to verify in UTF-8 format.
50
50
  * @param signature - The signature to verify in Base64 format.
51
51
  * @param options - The options for the operation.
52
- * @default {
53
- * algoType: SigningAlgorithm.ECDSA_SHA256,
54
- * }
55
52
  * @returns True if the signature is valid, false otherwise.
56
53
  */
57
54
  verify: (alias: string, data: string, signature: string, options?: VerifyOptions) => Promise<boolean | null>;
@@ -71,12 +68,6 @@ declare const _default: {
71
68
  * @param alias - The alias to use for the key pair.
72
69
  * @param data - The data to decrypt in Base64 format.
73
70
  * @param options - The options for the operation.
74
- * @default {
75
- * algoType: EncryptionAlgorithm.RSA_2048_PKCS1,
76
- * promptTitle: "Unlock",
77
- * promptSubtitle: "Enter your PIN to continue",
78
- * authMethod: AuthMethod.PASSCODE_OR_BIOMETRIC
79
- * }
80
71
  * @returns The decrypted data.
81
72
  */
82
73
  decrypt: (alias: string, data: string, options?: DecryptOptions) => Promise<string | null>;
@@ -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;;IA6B5B;;;OAGG;gCAtBqB,eAAe;IAwBvC;;;;OAIG;6BAC4B,MAAM,YAAY,sBAAsB;IAQvE;;;;;OAKG;0BACmB,MAAM,YAAY,mBAAmB;IAQ3D;;;;OAIG;2BApDkB,MAAM,KAAG,OAAO;IAsDrC;;;OAGG;mBAxDQ,MAAM,EAAE;IA0DnB;;;;;;;;;;;;OAYG;kBACiB,MAAM,QAAQ,MAAM,YAAY,WAAW;IAS/D;;;;;;;;;;OAUG;oBAEM,MAAM,QACP,MAAM,aACD,MAAM,YACP,aAAa;IAOzB;;;;;;;;;OASG;qBACoB,MAAM,QAAQ,MAAM,YAAY,cAAc;IAMrE;;;;;;;;;;;;OAYG;qBACoB,MAAM,QAAQ,MAAM,YAAY,cAAc;;AArHvE,wBA8HE"}
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"}
@@ -12,6 +12,12 @@ export default {
12
12
  * @returns The result of the operation.
13
13
  */
14
14
  isAuthCheckAvailable: module.isAuthCheckAvailable,
15
+ /**
16
+ * Checks if the strong box is available .
17
+ * @returns The result of the operation.
18
+ * @platform android 🤖
19
+ */
20
+ isStrongBoxAvailable: module.isStrongBoxAvailable,
15
21
  /**
16
22
  * Creates a new ECDSA P‑256 key pair for the given alias, if it doesn’t already exist.
17
23
  * @param alias - The alias to use for the key pair.
@@ -22,6 +28,7 @@ export default {
22
28
  algoType: options?.algorithmType,
23
29
  reqAuth: options?.requireAuthentication ?? false,
24
30
  authMethod: options?.authMethod ?? AuthMethod.PASSCODE_OR_BIOMETRIC,
31
+ preferStrongBox: options?.preferStrongBox ?? false,
25
32
  };
26
33
  return module.generateKeyPair(alias, o);
27
34
  },
@@ -56,12 +63,6 @@ export default {
56
63
  * @param alias - The alias to use for the key pair.
57
64
  * @param data - The data to sign.
58
65
  * @param options - The options for the operation.
59
- * @default {
60
- * algoType: SigningAlgorithm.ECDSA_SHA256,
61
- * promptTitle: "Unlock",
62
- * promptSubtitle: "Enter your PIN to continue",
63
- * authMethod: AuthMethod.PASSCODE_OR_BIOMETRIC
64
- * }
65
66
  * @returns The signature.
66
67
  */
67
68
  sign: async (alias, data, options) => {
@@ -79,9 +80,6 @@ export default {
79
80
  * @param data - The data to verify in UTF-8 format.
80
81
  * @param signature - The signature to verify in Base64 format.
81
82
  * @param options - The options for the operation.
82
- * @default {
83
- * algoType: SigningAlgorithm.ECDSA_SHA256,
84
- * }
85
83
  * @returns True if the signature is valid, false otherwise.
86
84
  */
87
85
  verify: async (alias, data, signature, options) => {
@@ -111,12 +109,6 @@ export default {
111
109
  * @param alias - The alias to use for the key pair.
112
110
  * @param data - The data to decrypt in Base64 format.
113
111
  * @param options - The options for the operation.
114
- * @default {
115
- * algoType: EncryptionAlgorithm.RSA_2048_PKCS1,
116
- * promptTitle: "Unlock",
117
- * promptSubtitle: "Enter your PIN to continue",
118
- * authMethod: AuthMethod.PASSCODE_OR_BIOMETRIC
119
- * }
120
112
  * @returns The decrypted data.
121
113
  */
122
114
  decrypt: async (alias, data, options) => {
@@ -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 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 * 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 };\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 * @default { \n * algoType: SigningAlgorithm.ECDSA_SHA256, \n * promptTitle: \"Unlock\", \n * promptSubtitle: \"Enter your PIN to continue\", \n * authMethod: AuthMethod.PASSCODE_OR_BIOMETRIC \n * }\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 * @default { \n * algoType: SigningAlgorithm.ECDSA_SHA256, \n * }\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 * @default { \n * algoType: EncryptionAlgorithm.RSA_2048_PKCS1, \n * promptTitle: \"Unlock\", \n * promptSubtitle: \"Enter your PIN to continue\", \n * authMethod: AuthMethod.PASSCODE_OR_BIOMETRIC \n * }\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 }\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,6 +1,6 @@
1
1
  Pod::Spec.new do |s|
2
2
  s.name = 'DeviceCrypto'
3
- s.version = '0.2.0'
3
+ s.version = '0.2.2'
4
4
  s.summary = 'Device crypto'
5
5
  s.description = 'Device crypto for Expo using device hardware capabilities'
6
6
  s.author = 'Piotr Pietras'
@@ -103,38 +103,6 @@ public class DeviceCryptoModule: Module {
103
103
  return Data([0x80 | UInt8(bytes.count)]) + Data(bytes)
104
104
  }
105
105
 
106
- private func isAuthCheckAvailable() -> String {
107
- let context = LAContext()
108
- let available = context.canEvaluatePolicy(.deviceOwnerAuthentication, error: nil)
109
- if available {
110
- return AuthCheckResult.AVAILABLE.rawValue
111
- } else {
112
- return AuthCheckResult.UNAVAILABLE.rawValue
113
- }
114
- }
115
-
116
- private func getAliases() -> [String] {
117
- let query: [String: Any] = [
118
- kSecClass as String: kSecClassKey,
119
- kSecAttrKeyClass as String: kSecAttrKeyClassPublic,
120
- kSecMatchLimit as String: kSecMatchLimitAll,
121
- kSecReturnAttributes as String: true,
122
- ]
123
-
124
- var result: CFTypeRef?
125
- let status = SecItemCopyMatching(query as CFDictionary, &result)
126
- guard status == errSecSuccess else { return [] }
127
-
128
- let items = (result as? [[String: Any]]) ?? []
129
- return items.compactMap { attrs in
130
- let tagKey = kSecAttrApplicationTag as String
131
- if let tagString = attrs[tagKey] as? String {
132
- return tagString
133
- }
134
- return nil
135
- }
136
- }
137
-
138
106
  private func getSecKeyQuery(_ alias: String, keyClass: CFString, returnRef: Bool = true) -> [String: Any] {
139
107
  var query: [String: Any] = [
140
108
  kSecClass as String: kSecClassKey,
@@ -155,40 +123,6 @@ public class DeviceCryptoModule: Module {
155
123
  return (item as! SecKey)
156
124
  }
157
125
 
158
- private func retrievePublicKey(_ secKey: SecKey) -> String? {
159
- let publicKey = SecKeyCopyPublicKey(secKey)!
160
-
161
- guard
162
- let attrs = SecKeyCopyAttributes(publicKey) as? [String: Any],
163
- let keyType = attrs[kSecAttrKeyType as String] as? String
164
- else {
165
- return nil
166
- }
167
-
168
- if keyType == (kSecAttrKeyTypeECSECPrimeRandom as String) {
169
- return x962ECPointToP256SPKI(publicKey)?.base64EncodedString()
170
- }
171
-
172
- if keyType == (kSecAttrKeyTypeRSA as String) {
173
- return rsaPKCS1ToSPKI(publicKey)?.base64EncodedString()
174
- }
175
-
176
- guard let publicKeyData = SecKeyCopyExternalRepresentation(publicKey, nil) as Data? else {
177
- return nil
178
- }
179
- return publicKeyData.base64EncodedString()
180
- }
181
-
182
- private func removeKeyStoreEntry(_ alias: String) -> Bool {
183
- let privateQuery = self.getSecKeyQuery(alias, keyClass: kSecAttrKeyClassPrivate, returnRef: false)
184
- let privateStatus = SecItemDelete(privateQuery as CFDictionary)
185
-
186
- let publicQuery = self.getSecKeyQuery(alias, keyClass: kSecAttrKeyClassPublic, returnRef: false)
187
- let publicStatus = SecItemDelete(publicQuery as CFDictionary)
188
-
189
- return privateStatus == errSecSuccess || publicStatus == errSecSuccess
190
- }
191
-
192
126
  private func buildECDSA(alias: String, reqAuth: Bool, authMethod: AuthMethod) -> SecKey? {
193
127
  let accessFlags: SecAccessControlCreateFlags
194
128
  if reqAuth {
@@ -264,48 +198,205 @@ public class DeviceCryptoModule: Module {
264
198
  return SecKeyCreateRandomKey(attributes, nil)
265
199
  }
266
200
 
267
- public func definition() -> ModuleDefinition {
201
+ private func isAuthCheckAvailable() -> String {
202
+ let context = LAContext()
203
+ let available = context.canEvaluatePolicy(.deviceOwnerAuthentication, error: nil)
204
+ if available {
205
+ return AuthCheckResult.AVAILABLE.rawValue
206
+ } else {
207
+ return AuthCheckResult.UNAVAILABLE.rawValue
208
+ }
209
+ }
268
210
 
269
- Name("DeviceCrypto")
211
+ private func generateKeyPair(alias: String, o: [String: Any]) throws -> String {
212
+ let reqAuth = o["reqAuth"] as! Bool
213
+ let authMethod = AuthMethod(rawValue: o["authMethod"] as! String)
214
+ let algoType = AlgorithmType(rawValue: o["algoType"] as! String)
270
215
 
271
- Function("isAuthCheckAvailable") { () -> String in
272
- return self.isAuthCheckAvailable()
216
+ if reqAuth && self.isAuthCheckAvailable() != AuthCheckResult.AVAILABLE.rawValue {
217
+ throw NSError(
218
+ domain: "DeviceCrypto",
219
+ code: 1,
220
+ userInfo: [NSLocalizedDescriptionKey: "NO_AUTH_AVAILABLE"]
221
+ )
273
222
  }
274
223
 
275
- Function("generateKeyPair") { (alias: String, o: [String: Any]) -> String in
276
- let reqAuth = o["reqAuth"] as! Bool
277
- let authMethod = AuthMethod(rawValue: o["authMethod"] as! String)
278
- let algoType = AlgorithmType(rawValue: o["algoType"] as! String)
224
+ let secKey = self.getSecKeyByAlias(alias, keyClass: kSecAttrKeyClassPrivate)
225
+ if secKey != nil {
226
+ return SecureSigningModuleResult.KEY_PAIR_ALREADY_EXISTS.rawValue
227
+ }
279
228
 
280
- if reqAuth && self.isAuthCheckAvailable() != AuthCheckResult.AVAILABLE.rawValue {
229
+ switch algoType {
230
+ case .ECDSA_SECP256R1_SHA256:
231
+ self.buildECDSA(alias: alias, reqAuth: reqAuth, authMethod: authMethod!)
232
+ case .RSA_2048_PKCS1:
233
+ self.buildRSA(alias: alias, reqAuth: reqAuth, authMethod: authMethod!)
234
+ case .RSA_2048_OAEP_SHA1:
235
+ self.buildRSA(alias: alias, reqAuth: reqAuth, authMethod: authMethod!)
236
+ default:
281
237
  throw NSError(
282
238
  domain: "DeviceCrypto",
283
239
  code: 1,
284
- userInfo: [NSLocalizedDescriptionKey: "NO_AUTH_AVAILABLE"]
240
+ userInfo: [NSLocalizedDescriptionKey: "INVALID_ALGORITHM_TYPE"]
285
241
  )
286
- }
242
+ }
287
243
 
288
- let secKey = self.getSecKeyByAlias(alias, keyClass: kSecAttrKeyClassPrivate)
289
- if secKey != nil {
290
- return SecureSigningModuleResult.KEY_PAIR_ALREADY_EXISTS.rawValue
291
- }
244
+ return SecureSigningModuleResult.KEY_PAIR_GENERATED.rawValue
245
+ }
246
+
247
+ private func removeKeyStoreEntry(_ alias: String) -> Bool {
248
+ let privateQuery = self.getSecKeyQuery(alias, keyClass: kSecAttrKeyClassPrivate, returnRef: false)
249
+ let privateStatus = SecItemDelete(privateQuery as CFDictionary)
250
+
251
+ let publicQuery = self.getSecKeyQuery(alias, keyClass: kSecAttrKeyClassPublic, returnRef: false)
252
+ let publicStatus = SecItemDelete(publicQuery as CFDictionary)
253
+
254
+ return privateStatus == errSecSuccess || publicStatus == errSecSuccess
255
+ }
292
256
 
293
- switch algoType {
294
- case .ECDSA_SECP256R1_SHA256:
295
- self.buildECDSA(alias: alias, reqAuth: reqAuth, authMethod: authMethod!)
296
- case .RSA_2048_PKCS1:
297
- self.buildRSA(alias: alias, reqAuth: reqAuth, authMethod: authMethod!)
298
- case .RSA_2048_OAEP_SHA1:
299
- self.buildRSA(alias: alias, reqAuth: reqAuth, authMethod: authMethod!)
300
- default:
301
- throw NSError(
302
- domain: "DeviceCrypto",
303
- code: 1,
304
- userInfo: [NSLocalizedDescriptionKey: "INVALID_ALGORITHM_TYPE"]
305
- )
257
+ private func getAliases() -> [String] {
258
+ let query: [String: Any] = [
259
+ kSecClass as String: kSecClassKey,
260
+ kSecAttrKeyClass as String: kSecAttrKeyClassPublic,
261
+ kSecMatchLimit as String: kSecMatchLimitAll,
262
+ kSecReturnAttributes as String: true,
263
+ ]
264
+
265
+ var result: CFTypeRef?
266
+ let status = SecItemCopyMatching(query as CFDictionary, &result)
267
+ guard status == errSecSuccess else { return [] }
268
+
269
+ let items = (result as? [[String: Any]]) ?? []
270
+ return items.compactMap { attrs in
271
+ let tagKey = kSecAttrApplicationTag as String
272
+ if let tagString = attrs[tagKey] as? String {
273
+ return tagString
306
274
  }
275
+ return nil
276
+ }
277
+ }
278
+
279
+ private func getPublicKey(alias: String) -> String? {
280
+ let secKey = self.getSecKeyByAlias(alias, keyClass: kSecAttrKeyClassPublic)
281
+ guard let secKey else { return nil }
282
+
283
+ let publicKey = SecKeyCopyPublicKey(secKey)!
284
+
285
+ guard
286
+ let attrs = SecKeyCopyAttributes(publicKey) as? [String: Any],
287
+ let keyType = attrs[kSecAttrKeyType as String] as? String
288
+ else {
289
+ return nil
290
+ }
291
+
292
+ if keyType == (kSecAttrKeyTypeECSECPrimeRandom as String) {
293
+ return x962ECPointToP256SPKI(publicKey)?.base64EncodedString()
294
+ }
295
+
296
+ if keyType == (kSecAttrKeyTypeRSA as String) {
297
+ return rsaPKCS1ToSPKI(publicKey)?.base64EncodedString()
298
+ }
299
+
300
+ guard let publicKeyData = SecKeyCopyExternalRepresentation(publicKey, nil) as Data? else {
301
+ return nil
302
+ }
303
+ return publicKeyData.base64EncodedString()
304
+ }
305
+
306
+ private func sign(alias: String, data: String, o: [String: Any]) throws -> String? {
307
+ let secKey = self.getSecKeyByAlias(alias, keyClass: kSecAttrKeyClassPrivate)
308
+ guard let secKey else { return nil }
307
309
 
308
- return SecureSigningModuleResult.KEY_PAIR_GENERATED.rawValue
310
+ let algoType = AlgorithmType(rawValue: o["algoType"] as! String)
311
+ let algo = self.toiOSAlgo(algorithm: algoType!)
312
+
313
+ var signingError: Unmanaged<CFError>?
314
+ let signatureCF = SecKeyCreateSignature(
315
+ secKey,
316
+ algo,
317
+ Data(data.utf8) as CFData,
318
+ &signingError
319
+ )
320
+
321
+ if let error = signingError?.takeRetainedValue() {
322
+ throw error as Error
323
+ }
324
+
325
+ guard let signatureCF else { return nil }
326
+ let signature = signatureCF as Data
327
+ return signature.base64EncodedString()
328
+ }
329
+
330
+ private func verify(alias: String, data: String, signature: String, o: [String: Any]) -> Bool? {
331
+ let secKey = self.getSecKeyByAlias(alias, keyClass: kSecAttrKeyClassPrivate)
332
+ guard let secKey else { return nil }
333
+
334
+ guard let publicKey = SecKeyCopyPublicKey(secKey) else { return nil }
335
+ guard let signatureData = Data(base64Encoded: signature) else { return nil }
336
+
337
+ let algoType = AlgorithmType(rawValue: o["algoType"] as! String)
338
+ let algo = self.toiOSAlgo(algorithm: algoType!)
339
+
340
+ let valid = SecKeyVerifySignature(
341
+ publicKey,
342
+ algo,
343
+ Data(data.utf8) as CFData,
344
+ signatureData as CFData,
345
+ nil
346
+ )
347
+ return valid
348
+ }
349
+
350
+ private func encrypt(alias: String, data: String, o: [String: Any]) -> String? {
351
+ let secKey = self.getSecKeyByAlias(alias, keyClass: kSecAttrKeyClassPublic)
352
+ guard let secKey else { return nil }
353
+ let publicKey = SecKeyCopyPublicKey(secKey)!
354
+
355
+ let algoType = AlgorithmType(rawValue: o["algoType"] as! String)
356
+ let algo = self.toiOSAlgo(algorithm: algoType!)
357
+
358
+ guard let encrypted = SecKeyCreateEncryptedData(
359
+ publicKey,
360
+ algo,
361
+ Data(data.utf8) as CFData,
362
+ nil
363
+ ) as Data? else {
364
+ return nil
365
+ }
366
+
367
+ return encrypted.base64EncodedString()
368
+ }
369
+
370
+ private func decrypt(alias: String, data: String, o: [String: Any]) -> String? {
371
+ let secKey = self.getSecKeyByAlias(alias, keyClass: kSecAttrKeyClassPrivate)
372
+ guard let secKey else { return nil }
373
+ guard let encryptedData = Data(base64Encoded: data) else { return nil }
374
+
375
+ let algoType = AlgorithmType(rawValue: o["algoType"] as! String)
376
+ let algo = self.toiOSAlgo(algorithm: algoType!)
377
+
378
+ guard let decrypted = SecKeyCreateDecryptedData(
379
+ secKey,
380
+ algo,
381
+ encryptedData as CFData,
382
+ nil
383
+ ) as Data? else {
384
+ return nil
385
+ }
386
+
387
+ return String(data: decrypted, encoding: .utf8)
388
+ }
389
+
390
+ public func definition() -> ModuleDefinition {
391
+
392
+ Name("DeviceCrypto")
393
+
394
+ Function("isAuthCheckAvailable") { () -> String in
395
+ return self.isAuthCheckAvailable()
396
+ }
397
+
398
+ Function("generateKeyPair") { (alias: String, o: [String: Any]) -> String in
399
+ return try self.generateKeyPair(alias: alias, o: o)
309
400
  }
310
401
 
311
402
  Function("removeKeyPair") { (alias: String) -> Bool in
@@ -317,93 +408,23 @@ public class DeviceCryptoModule: Module {
317
408
  }
318
409
 
319
410
  Function("getPublicKey") { (alias: String) -> String? in
320
- let secKey = self.getSecKeyByAlias(alias, keyClass: kSecAttrKeyClassPublic)
321
- guard let secKey else { return nil }
322
- return self.retrievePublicKey(secKey)
411
+ return self.getPublicKey(alias:alias)
323
412
  }
324
413
 
325
414
  AsyncFunction("sign") { (alias: String, data: String, o: [String: Any]) -> String? in
326
- let secKey = self.getSecKeyByAlias(alias, keyClass: kSecAttrKeyClassPrivate)
327
- guard let secKey else { return nil }
328
-
329
- let algoType = AlgorithmType(rawValue: o["algoType"] as! String)
330
- let algo = self.toiOSAlgo(algorithm: algoType!)
331
-
332
- var signingError: Unmanaged<CFError>?
333
- let signatureCF = SecKeyCreateSignature(
334
- secKey,
335
- algo,
336
- Data(data.utf8) as CFData,
337
- &signingError
338
- )
339
-
340
- if let error = signingError?.takeRetainedValue() {
341
- throw error as Error
342
- }
343
-
344
- guard let signatureCF else { return nil }
345
- let signature = signatureCF as Data
346
- return signature.base64EncodedString()
415
+ return try self.sign(alias: alias, data: data, o: o)
347
416
  }
348
417
 
349
418
  Function("verify") { (alias: String, data: String, signature: String, o: [String: Any]) -> Bool? in
350
- let secKey = self.getSecKeyByAlias(alias, keyClass: kSecAttrKeyClassPrivate)
351
- guard let secKey else { return nil }
352
-
353
- guard let publicKey = SecKeyCopyPublicKey(secKey) else { return nil }
354
- guard let signatureData = Data(base64Encoded: signature) else { return nil }
355
-
356
- let algoType = AlgorithmType(rawValue: o["algoType"] as! String)
357
- let algo = self.toiOSAlgo(algorithm: algoType!)
358
-
359
- let valid = SecKeyVerifySignature(
360
- publicKey,
361
- algo,
362
- Data(data.utf8) as CFData,
363
- signatureData as CFData,
364
- nil
365
- )
366
- return valid
419
+ return self.verify(alias: alias, data: data, signature: signature, o: o)
367
420
  }
368
421
 
369
422
  AsyncFunction("encrypt") { (alias: String, data: String, o: [String: Any]) -> String? in
370
- let secKey = self.getSecKeyByAlias(alias, keyClass: kSecAttrKeyClassPublic)
371
- guard let secKey else { return nil }
372
- let publicKey = SecKeyCopyPublicKey(secKey)!
373
-
374
- let algoType = AlgorithmType(rawValue: o["algoType"] as! String)
375
- let algo = self.toiOSAlgo(algorithm: algoType!)
376
-
377
- guard let encrypted = SecKeyCreateEncryptedData(
378
- publicKey,
379
- algo,
380
- Data(data.utf8) as CFData,
381
- nil
382
- ) as Data? else {
383
- return nil
384
- }
385
-
386
- return encrypted.base64EncodedString()
423
+ return self.encrypt(alias: alias, data: data, o: o)
387
424
  }
388
425
 
389
426
  AsyncFunction("decrypt") { (alias: String, data: String, o: [String: Any]) -> String? in
390
- let secKey = self.getSecKeyByAlias(alias, keyClass: kSecAttrKeyClassPrivate)
391
- guard let secKey else { return nil }
392
- guard let encryptedData = Data(base64Encoded: data) else { return nil }
393
-
394
- let algoType = AlgorithmType(rawValue: o["algoType"] as! String)
395
- let algo = self.toiOSAlgo(algorithm: algoType!)
396
-
397
- guard let decrypted = SecKeyCreateDecryptedData(
398
- secKey,
399
- algo,
400
- encryptedData as CFData,
401
- nil
402
- ) as Data? else {
403
- return nil
404
- }
405
-
406
- return String(data: decrypted, encoding: .utf8)
427
+ return self.decrypt(alias: alias, data: data, o: o)
407
428
  }
408
429
  }
409
430
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "expo-device-crypto",
3
- "version": "0.2.0",
3
+ "version": "0.2.2",
4
4
  "description": "Crypto for Expo using device hardware capabilities",
5
5
  "main": "build/index.js",
6
6
  "types": "build/index.d.ts",
@@ -51,29 +51,35 @@ export interface GenerateKeyPairOptions {
51
51
  * }
52
52
  * ```
53
53
  * @default AuthMethod.PASSCODE_OR_BIOMETRIC
54
- * @platform ios
54
+ * @platform ios 🍏
55
55
  */
56
56
  authMethod?: AuthMethod;
57
+ /**
58
+ * If the Strong Box is available, it will be used instead of the Trusted Execution Environment (TEE).
59
+ * @default false
60
+ * @platform android 🤖
61
+ */
62
+ preferStrongBox?: boolean;
57
63
  }
58
64
 
59
65
  interface BaseSignDecryptOptions {
60
66
  /**
61
67
  * The title of the prompt to show when authentication is required.
62
68
  * @default "Unlock"
63
- * @platform android
69
+ * @platform android 🤖
64
70
  */
65
71
  promptTitle?: string;
66
72
  /**
67
73
  * The subtitle of the prompt to show when authentication is required.
68
74
  * @default "Enter your PIN to continue"
69
- * @platform android
75
+ * @platform android 🤖
70
76
  */
71
77
  promptSubtitle?: string;
72
78
  /**
73
79
  * The method of authentication to use.
74
80
  * Note that on iOS you have to define it when generating the key pair.
75
81
  * @default AuthMethod.PASSCODE_OR_BIOMETRIC
76
- * @platform android
82
+ * @platform android 🤖
77
83
  */
78
84
  authMethod?: AuthMethod;
79
85
  }
@@ -23,6 +23,7 @@ function base64ToPem(base64: string, label = "DATA") {
23
23
 
24
24
  declare class DeviceCryptoModule extends NativeModule {
25
25
  isAuthCheckAvailable(): AuthCheckResult;
26
+ isStrongBoxAvailable(): boolean;
26
27
  generateKeyPair(alias: string, o: any): GenerateKeyPairResult;
27
28
  getPublicKey(alias: string): string | null;
28
29
  removeKeyPair(alias: string): boolean;
@@ -46,6 +47,12 @@ export default {
46
47
  * @returns The result of the operation.
47
48
  */
48
49
  isAuthCheckAvailable: module.isAuthCheckAvailable,
50
+ /**
51
+ * Checks if the strong box is available .
52
+ * @returns The result of the operation.
53
+ * @platform android 🤖
54
+ */
55
+ isStrongBoxAvailable: module.isStrongBoxAvailable,
49
56
  /**
50
57
  * Creates a new ECDSA P‑256 key pair for the given alias, if it doesn’t already exist.
51
58
  * @param alias - The alias to use for the key pair.
@@ -56,6 +63,7 @@ export default {
56
63
  algoType: options?.algorithmType,
57
64
  reqAuth: options?.requireAuthentication ?? false,
58
65
  authMethod: options?.authMethod ?? AuthMethod.PASSCODE_OR_BIOMETRIC,
66
+ preferStrongBox: options?.preferStrongBox ?? false,
59
67
  };
60
68
  return module.generateKeyPair(alias, o);
61
69
  },
@@ -89,12 +97,6 @@ export default {
89
97
  * @param alias - The alias to use for the key pair.
90
98
  * @param data - The data to sign.
91
99
  * @param options - The options for the operation.
92
- * @default {
93
- * algoType: SigningAlgorithm.ECDSA_SHA256,
94
- * promptTitle: "Unlock",
95
- * promptSubtitle: "Enter your PIN to continue",
96
- * authMethod: AuthMethod.PASSCODE_OR_BIOMETRIC
97
- * }
98
100
  * @returns The signature.
99
101
  */
100
102
  sign: async (alias: string, data: string, options?: SignOptions) => {
@@ -112,9 +114,6 @@ export default {
112
114
  * @param data - The data to verify in UTF-8 format.
113
115
  * @param signature - The signature to verify in Base64 format.
114
116
  * @param options - The options for the operation.
115
- * @default {
116
- * algoType: SigningAlgorithm.ECDSA_SHA256,
117
- * }
118
117
  * @returns True if the signature is valid, false otherwise.
119
118
  */
120
119
  verify: async (
@@ -149,12 +148,6 @@ export default {
149
148
  * @param alias - The alias to use for the key pair.
150
149
  * @param data - The data to decrypt in Base64 format.
151
150
  * @param options - The options for the operation.
152
- * @default {
153
- * algoType: EncryptionAlgorithm.RSA_2048_PKCS1,
154
- * promptTitle: "Unlock",
155
- * promptSubtitle: "Enter your PIN to continue",
156
- * authMethod: AuthMethod.PASSCODE_OR_BIOMETRIC
157
- * }
158
151
  * @returns The decrypted data.
159
152
  */
160
153
  decrypt: async (alias: string, data: string, options?: DecryptOptions) => {