expo-device-crypto 0.2.0 → 0.2.1
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) 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,10 @@ 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
|
+
> ✅ JSDoc type definitions are available in `./src/DeviceCryptoModule.ts`.
|
|
180
|
+
|
|
181
|
+
## Important
|
|
182
|
+
|
|
183
|
+
### RSA algorithms on iOS
|
|
184
|
+
|
|
185
|
+
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.
|
package/android/build.gradle
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
apply plugin: 'com.android.library'
|
|
2
2
|
|
|
3
3
|
group = 'expo.modules.devicecrypto'
|
|
4
|
-
version = '0.2.
|
|
4
|
+
version = '0.2.1'
|
|
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.
|
|
38
|
+
versionName "0.2.1"
|
|
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
|
|
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,23 +106,6 @@ 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,
|
|
@@ -249,191 +218,229 @@ class DeviceCryptoModule : Module() {
|
|
|
249
218
|
}
|
|
250
219
|
}
|
|
251
220
|
|
|
252
|
-
|
|
253
|
-
|
|
221
|
+
private fun isAuthCheckAvailable(): AuthCheckResult {
|
|
222
|
+
val biometricManager = BiometricManager.from(appContext.reactContext as Context)
|
|
223
|
+
return when (biometricManager.canAuthenticate(BIOMETRIC_STRONG or DEVICE_CREDENTIAL)) {
|
|
224
|
+
BiometricManager.BIOMETRIC_SUCCESS -> AuthCheckResult.AVAILABLE
|
|
225
|
+
BiometricManager.BIOMETRIC_ERROR_NO_HARDWARE -> AuthCheckResult.NO_HARDWARE
|
|
226
|
+
BiometricManager.BIOMETRIC_ERROR_HW_UNAVAILABLE -> AuthCheckResult.UNAVAILABLE
|
|
227
|
+
else -> AuthCheckResult.UNAVAILABLE
|
|
228
|
+
}
|
|
229
|
+
}
|
|
254
230
|
|
|
255
|
-
|
|
256
|
-
|
|
231
|
+
private fun generateKeyPair(alias: String, o: Map<String, Any?>): GenerateKeyPairResult {
|
|
232
|
+
val reqAuth = o["reqAuth"] as Boolean
|
|
233
|
+
if (reqAuth && isAuthCheckAvailable() != AuthCheckResult.AVAILABLE) {
|
|
234
|
+
throw Exception("NO_AUTH_AVAILABLE")
|
|
235
|
+
}
|
|
236
|
+
if (!isKeyStoreAvailable()) {
|
|
237
|
+
return GenerateKeyPairResult.NOT_AVAILABLE
|
|
238
|
+
}
|
|
239
|
+
if (getKeyStoreEntry(alias) != null) {
|
|
240
|
+
return GenerateKeyPairResult.KEY_PAIR_ALREADY_EXISTS
|
|
257
241
|
}
|
|
258
242
|
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
}
|
|
267
|
-
if (getKeyStoreEntry(alias) != null) {
|
|
268
|
-
return@Function GenerateKeyPairResult.KEY_PAIR_ALREADY_EXISTS
|
|
269
|
-
}
|
|
243
|
+
val algoType = AlgorithmType.valueOf(o["algoType"] as String)
|
|
244
|
+
val kpg = when (algoType) {
|
|
245
|
+
AlgorithmType.ECDSA_SECP256R1_SHA256 -> buildECDSA(alias, DIGEST_SHA256, reqAuth)
|
|
246
|
+
AlgorithmType.RSA_2048_PKCS1 -> buildRSA(alias, null, ENCRYPTION_PADDING_RSA_PKCS1, reqAuth)
|
|
247
|
+
AlgorithmType.RSA_2048_OAEP_SHA1 -> buildRSA(alias, DIGEST_SHA1, ENCRYPTION_PADDING_RSA_OAEP, reqAuth)
|
|
248
|
+
else -> throw Exception("INVALID_ALGORITHM_TYPE")
|
|
249
|
+
}
|
|
270
250
|
|
|
271
|
-
|
|
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
|
-
}
|
|
251
|
+
kpg.generateKeyPair()
|
|
278
252
|
|
|
279
|
-
|
|
253
|
+
return GenerateKeyPairResult.KEY_PAIR_GENERATED
|
|
254
|
+
}
|
|
280
255
|
|
|
281
|
-
|
|
256
|
+
private fun removeKeyStoreEntry(alias: String): Boolean {
|
|
257
|
+
val ks = KeyStore.getInstance("AndroidKeyStore").apply {
|
|
258
|
+
load(null)
|
|
282
259
|
}
|
|
260
|
+
if (ks.getEntry(alias, null) == null) return false
|
|
283
261
|
|
|
284
|
-
|
|
285
|
-
|
|
262
|
+
ks.deleteEntry(alias)
|
|
263
|
+
return true
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
private fun getAliases() = KeyStore.getInstance("AndroidKeyStore").apply {
|
|
267
|
+
load(null)
|
|
268
|
+
}.aliases().toList()
|
|
269
|
+
|
|
270
|
+
private fun getPublicKey(alias: String): String? {
|
|
271
|
+
val ks = getKeyStoreEntry(alias)
|
|
272
|
+
if (ks !is KeyStore.PrivateKeyEntry) return null
|
|
273
|
+
|
|
274
|
+
val publicKeyBytes: ByteArray = ks.certificate?.publicKey?.encoded ?: return null
|
|
275
|
+
return Base64.encodeToString(publicKeyBytes, Base64.NO_WRAP)
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
private fun sign(alias: String, data: String, o: Map<String, Any?>, promise: Promise) {
|
|
279
|
+
val entry = getKeyStoreEntry(alias)
|
|
280
|
+
if (entry !is KeyStore.PrivateKeyEntry) {
|
|
281
|
+
promise.resolve(null)
|
|
282
|
+
return
|
|
286
283
|
}
|
|
287
284
|
|
|
288
|
-
|
|
289
|
-
|
|
285
|
+
val reqAuth = isKeyStoreRequireAuthentication(entry)
|
|
286
|
+
|
|
287
|
+
val title = o["title"] as String
|
|
288
|
+
val subtitle = o["subtitle"] as String
|
|
289
|
+
val authMethod = AuthMethod.valueOf(o["authMethod"] as String)
|
|
290
|
+
val algoType = AlgorithmType.valueOf(o["algoType"] as String)
|
|
291
|
+
val algo = toAndroidAlgo(algoType)
|
|
292
|
+
|
|
293
|
+
if (!reqAuth) {
|
|
294
|
+
val signature: ByteArray = Signature.getInstance(algo).apply {
|
|
295
|
+
initSign(entry.privateKey)
|
|
296
|
+
update(data.toByteArray(Charsets.UTF_8))
|
|
297
|
+
}.sign()
|
|
298
|
+
promise.resolve(Base64.encodeToString(signature, Base64.NO_WRAP))
|
|
299
|
+
return
|
|
300
|
+
} else {
|
|
301
|
+
showAuthPrompt(
|
|
302
|
+
onSuccess = {
|
|
303
|
+
val signature: ByteArray = Signature.getInstance(algo).apply {
|
|
304
|
+
initSign(entry.privateKey)
|
|
305
|
+
update(data.toByteArray(Charsets.UTF_8))
|
|
306
|
+
}.sign()
|
|
307
|
+
promise.resolve(Base64.encodeToString(signature, Base64.NO_WRAP))
|
|
308
|
+
},
|
|
309
|
+
onError = { error ->
|
|
310
|
+
promise.reject("ERR_AUTH_FAILED", error, null)
|
|
311
|
+
},
|
|
312
|
+
title = title,
|
|
313
|
+
subtitle = subtitle,
|
|
314
|
+
authMethod = authMethod
|
|
315
|
+
)
|
|
290
316
|
}
|
|
317
|
+
}
|
|
291
318
|
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
319
|
+
private fun verify(alias: String, data: String, signature: String, o: Map<String, Any?>): Boolean? {
|
|
320
|
+
val entry = getKeyStoreEntry(alias)
|
|
321
|
+
if (entry !is KeyStore.PrivateKeyEntry) return null
|
|
322
|
+
|
|
323
|
+
val algoType = AlgorithmType.valueOf(o["algoType"] as String)
|
|
324
|
+
val algo = toAndroidAlgo(algoType)
|
|
325
|
+
|
|
326
|
+
val valid: Boolean = Signature.getInstance(algo).apply {
|
|
327
|
+
initVerify(entry.certificate)
|
|
328
|
+
update(data.toByteArray(Charsets.UTF_8))
|
|
329
|
+
}.verify(Base64.decode(signature, Base64.NO_WRAP))
|
|
330
|
+
return valid
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
private fun encrypt(alias: String, data: String, o: Map<String, Any?>, promise: Promise) {
|
|
334
|
+
val entry = getKeyStoreEntry(alias)
|
|
335
|
+
if (entry !is KeyStore.PrivateKeyEntry) {
|
|
336
|
+
promise.resolve(null)
|
|
337
|
+
return
|
|
295
338
|
}
|
|
296
339
|
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
promise: Promise ->
|
|
301
|
-
val entry = getKeyStoreEntry(alias)
|
|
302
|
-
if (entry !is KeyStore.PrivateKeyEntry) {
|
|
303
|
-
promise.resolve(null)
|
|
304
|
-
return@AsyncFunction
|
|
305
|
-
}
|
|
340
|
+
val algoType = AlgorithmType.valueOf(o["algoType"] as String)
|
|
341
|
+
val algo = toAndroidAlgo(algoType)
|
|
342
|
+
val oaepSpec = getOaepSpec(algoType)
|
|
306
343
|
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
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
|
-
}
|
|
344
|
+
val cipher = Cipher.getInstance(algo)
|
|
345
|
+
if (oaepSpec != null) {
|
|
346
|
+
cipher.init(Cipher.ENCRYPT_MODE, entry.certificate.publicKey, oaepSpec)
|
|
347
|
+
} else {
|
|
348
|
+
cipher.init(Cipher.ENCRYPT_MODE, entry.certificate.publicKey)
|
|
339
349
|
}
|
|
350
|
+
val encrypted: ByteArray = cipher.doFinal(data.toByteArray(Charsets.UTF_8))
|
|
351
|
+
promise.resolve(Base64.encodeToString(encrypted, Base64.NO_WRAP))
|
|
352
|
+
}
|
|
340
353
|
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
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
|
|
354
|
+
private fun decrypt(alias: String, data: String, o: Map<String, Any?>, promise: Promise) {
|
|
355
|
+
val entry = getKeyStoreEntry(alias)
|
|
356
|
+
if (entry !is KeyStore.PrivateKeyEntry) {
|
|
357
|
+
promise.resolve(null)
|
|
358
|
+
return
|
|
356
359
|
}
|
|
357
360
|
|
|
361
|
+
val reqAuth = isKeyStoreRequireAuthentication(entry)
|
|
358
362
|
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
promise.resolve(null)
|
|
366
|
-
return@AsyncFunction
|
|
367
|
-
}
|
|
363
|
+
val title = o["title"] as String
|
|
364
|
+
val subtitle = o["subtitle"] as String
|
|
365
|
+
val authMethod = o["authMethod"] as String
|
|
366
|
+
val algoType = AlgorithmType.valueOf(o["algoType"] as String)
|
|
367
|
+
val algo = toAndroidAlgo(algoType)
|
|
368
|
+
val oaepSpec = getOaepSpec(algoType)
|
|
368
369
|
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
val oaepSpec = getOaepSpec(algoType)
|
|
370
|
+
val cipher = Cipher.getInstance(algo)
|
|
371
|
+
val ciphertext = Base64.decode(data, Base64.NO_WRAP)
|
|
372
372
|
|
|
373
|
-
|
|
373
|
+
if (!reqAuth) {
|
|
374
374
|
if (oaepSpec != null) {
|
|
375
|
-
cipher.init(Cipher.
|
|
375
|
+
cipher.init(Cipher.DECRYPT_MODE, entry.privateKey, oaepSpec)
|
|
376
376
|
} else {
|
|
377
|
-
cipher.init(Cipher.
|
|
377
|
+
cipher.init(Cipher.DECRYPT_MODE, entry.privateKey)
|
|
378
378
|
}
|
|
379
|
-
val
|
|
380
|
-
promise.resolve(
|
|
379
|
+
val decrypted: ByteArray = cipher.doFinal(ciphertext)
|
|
380
|
+
promise.resolve(String(decrypted, Charsets.UTF_8))
|
|
381
|
+
return
|
|
382
|
+
} else {
|
|
383
|
+
showAuthPrompt(
|
|
384
|
+
onSuccess = {
|
|
385
|
+
if (oaepSpec != null) {
|
|
386
|
+
cipher.init(Cipher.DECRYPT_MODE, entry.privateKey, oaepSpec)
|
|
387
|
+
} else {
|
|
388
|
+
cipher.init(Cipher.DECRYPT_MODE, entry.privateKey)
|
|
389
|
+
}
|
|
390
|
+
val decrypted: ByteArray = cipher.doFinal(ciphertext)
|
|
391
|
+
promise.resolve(String(decrypted, Charsets.UTF_8))
|
|
392
|
+
},
|
|
393
|
+
onError = { error ->
|
|
394
|
+
promise.reject("ERR_AUTH_FAILED", error, null)
|
|
395
|
+
},
|
|
396
|
+
title = title,
|
|
397
|
+
subtitle = subtitle,
|
|
398
|
+
authMethod = when (authMethod) {
|
|
399
|
+
"PASSCODE" -> AuthMethod.PASSCODE
|
|
400
|
+
"PASSCODE_OR_BIOMETRIC" -> AuthMethod.PASSCODE_OR_BIOMETRIC
|
|
401
|
+
else -> AuthMethod.PASSCODE_OR_BIOMETRIC
|
|
402
|
+
}
|
|
403
|
+
)
|
|
381
404
|
}
|
|
405
|
+
}
|
|
382
406
|
|
|
383
|
-
|
|
384
|
-
|
|
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
|
-
}
|
|
407
|
+
override fun definition() = ModuleDefinition {
|
|
408
|
+
Name("DeviceCrypto")
|
|
392
409
|
|
|
393
|
-
|
|
410
|
+
Function("isAuthCheckAvailable") { ->
|
|
411
|
+
return@Function isAuthCheckAvailable()
|
|
412
|
+
}
|
|
394
413
|
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
val algoType = AlgorithmType.valueOf(o["algoType"] as String)
|
|
399
|
-
val algo = toAndroidAlgo(algoType)
|
|
400
|
-
val oaepSpec = getOaepSpec(algoType)
|
|
414
|
+
Function("generateKeyPair") { alias: String, o: Map<String, Any?> ->
|
|
415
|
+
return@Function generateKeyPair(alias, o)
|
|
416
|
+
}
|
|
401
417
|
|
|
402
|
-
|
|
403
|
-
|
|
418
|
+
Function("removeKeyPair") { alias: String ->
|
|
419
|
+
return@Function removeKeyStoreEntry(alias)
|
|
420
|
+
}
|
|
404
421
|
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
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
|
-
}
|
|
422
|
+
Function("aliases") {
|
|
423
|
+
return@Function getAliases()
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
Function("getPublicKey") { alias: String ->
|
|
427
|
+
return@Function getPublicKey(alias)
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
AsyncFunction("sign") { alias: String, data: String, o: Map<String, Any?>, promise: Promise ->
|
|
431
|
+
return@AsyncFunction sign(alias, data, o, promise)
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
Function("verify") { alias: String, data: String, signature: String, o: Map<String, Any?> ->
|
|
435
|
+
return@Function verify(alias, data, signature, o)
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
AsyncFunction("encrypt") { alias: String, data: String, o: Map<String, Any?>, promise: Promise ->
|
|
439
|
+
return@AsyncFunction encrypt(alias, data, o, promise)
|
|
440
|
+
}
|
|
441
|
+
|
|
442
|
+
AsyncFunction("decrypt") { alias: String, data: String, o: Map<String, Any?>, promise: Promise ->
|
|
443
|
+
return@AsyncFunction decrypt(alias, data, o, promise)
|
|
437
444
|
}
|
|
438
445
|
}
|
|
439
446
|
}
|
package/ios/DeviceCrypto.podspec
CHANGED
package/ios/DeviceCrypto.swift
CHANGED
|
@@ -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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
272
|
-
|
|
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
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
224
|
+
let secKey = self.getSecKeyByAlias(alias, keyClass: kSecAttrKeyClassPrivate)
|
|
225
|
+
if secKey != nil {
|
|
226
|
+
return SecureSigningModuleResult.KEY_PAIR_ALREADY_EXISTS.rawValue
|
|
227
|
+
}
|
|
279
228
|
|
|
280
|
-
|
|
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: "
|
|
240
|
+
userInfo: [NSLocalizedDescriptionKey: "INVALID_ALGORITHM_TYPE"]
|
|
285
241
|
)
|
|
286
|
-
|
|
242
|
+
}
|
|
287
243
|
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
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
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
}
|