expo-device-crypto 0.1.0 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +80 -33
- package/android/build.gradle +2 -2
- package/android/src/main/java/expo/modules/devicecrypto/DeviceCryptoModule.kt +80 -26
- package/build/DeviceCrypto.types.d.ts +2 -1
- package/build/DeviceCrypto.types.d.ts.map +1 -1
- package/build/DeviceCrypto.types.js +1 -0
- package/build/DeviceCrypto.types.js.map +1 -1
- package/build/DeviceCryptoModule.d.ts +2 -3
- package/build/DeviceCryptoModule.d.ts.map +1 -1
- package/build/DeviceCryptoModule.js +2 -2
- package/build/DeviceCryptoModule.js.map +1 -1
- package/ios/DeviceCrypto.podspec +1 -1
- package/ios/DeviceCrypto.swift +9 -4
- package/package.json +1 -1
- package/src/DeviceCrypto.types.ts +1 -0
- package/src/DeviceCryptoModule.ts +3 -5
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 and Apple 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).
|
|
6
6
|
|
|
7
7
|
## Installation
|
|
8
8
|
|
|
@@ -26,42 +26,35 @@ If you want to allow Face ID on iOS, add this to your app config:
|
|
|
26
26
|
|
|
27
27
|
## Supported Algorithms
|
|
28
28
|
|
|
29
|
-
###
|
|
29
|
+
### Signature Algorithms
|
|
30
30
|
|
|
31
|
-
-
|
|
32
|
-
- **Curve:** P-256 / secp256r1.
|
|
33
|
-
- **Hash:** SHA-256.
|
|
34
|
-
- **Use it for:** Authentication challenges, message signing, and tamper detection.
|
|
31
|
+
- #### `ECDSA_SECP256R1_SHA256`
|
|
35
32
|
|
|
36
|
-
|
|
33
|
+
**Curve:** P-256 / secp256r1. </br>
|
|
34
|
+
**Hash:** SHA-256. </br>
|
|
35
|
+
**Use it for:** Authentication challenges, message signing, and tamper detection.
|
|
37
36
|
|
|
38
|
-
|
|
39
|
-
- **Key size:** 2048 bits.
|
|
40
|
-
- **Padding:** PKCS#1 v1.5.
|
|
41
|
-
- **Use it for:** Encrypting small secrets that only the private key holder should decrypt.
|
|
37
|
+
### Encryption Algorithms
|
|
42
38
|
|
|
43
|
-
|
|
39
|
+
- #### `RSA_2048_PKCS1`
|
|
44
40
|
|
|
45
|
-
|
|
41
|
+
**Key size:** 2048 bits. </br>
|
|
42
|
+
**Padding:** PKCS#1 v1.5. </br>
|
|
43
|
+
**Use it for:** Encrypting small secrets that only the private key holder should decrypt.
|
|
46
44
|
|
|
47
|
-
-
|
|
48
|
-
- Use `AuthMethod.PASSCODE` or `AuthMethod.PASSCODE_OR_BIOMETRIC` to specify whether authentication should require only a passcode or allow biometrics when available.
|
|
45
|
+
- #### `RSA_2048_OAEP_SHA1`
|
|
49
46
|
|
|
50
|
-
|
|
47
|
+
**Key size:** 2048 bits. </br>
|
|
48
|
+
**Padding:** OAEP with SHA-1 and MGF1. </br>
|
|
49
|
+
**Use it for:** Encrypting small secrets with stronger padding than PKCS#1 v1.5 when interoperating with OAEP SHA-1 systems.
|
|
51
50
|
|
|
52
|
-
|
|
53
|
-
import DeviceCrypto, { AuthCheckResult } from "expo-device-crypto";
|
|
54
|
-
|
|
55
|
-
const authStatus = DeviceCrypto.isAuthCheckAvailable();
|
|
56
|
-
if (authStatus !== AuthCheckResult.AVAILABLE) {
|
|
57
|
-
throw new Error(`Authentication unavailable: ${authStatus}`);
|
|
58
|
-
}
|
|
59
|
-
```
|
|
51
|
+
More coming soon
|
|
60
52
|
|
|
61
53
|
## Example: ECDSA with Biometric
|
|
62
54
|
|
|
63
55
|
```ts
|
|
64
56
|
import DeviceCrypto, {
|
|
57
|
+
AuthCheckResult,
|
|
65
58
|
AuthMethod,
|
|
66
59
|
SigningAlgorithm,
|
|
67
60
|
} from "expo-device-crypto";
|
|
@@ -71,24 +64,31 @@ const payload = "Sign this challenge payload";
|
|
|
71
64
|
const algorithmType = SigningAlgorithm.ECDSA_SECP256R1_SHA256;
|
|
72
65
|
const authMethod = AuthMethod.PASSCODE_OR_BIOMETRIC;
|
|
73
66
|
|
|
74
|
-
// 1)
|
|
67
|
+
// 1) Ensure device authentication is configured (passcode at minimum)
|
|
68
|
+
const authStatus = DeviceCrypto.isAuthCheckAvailable();
|
|
69
|
+
if (authStatus !== AuthCheckResult.AVAILABLE) {
|
|
70
|
+
throw new Error(`Authentication unavailable: ${authStatus}`);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
// 2) Generate an auth-protected ECDSA key pair
|
|
75
74
|
await DeviceCrypto.generateKeyPair(alias, {
|
|
76
75
|
requireAuthentication: true,
|
|
77
76
|
authMethod, // iOS: defined at key generation;
|
|
78
77
|
algorithmType,
|
|
79
78
|
});
|
|
80
79
|
|
|
81
|
-
//
|
|
80
|
+
// 3) Optional: share public key with backend
|
|
82
81
|
const publicKeyPem = DeviceCrypto.getPublicKey(alias, { format: "PEM" });
|
|
83
82
|
|
|
84
|
-
//
|
|
83
|
+
// 4) Sign with private key (prompts biometric/passcode on protected keys)
|
|
85
84
|
// Note: The data to sign must be in UTF-8 format.
|
|
85
|
+
// Note: This function should display the system user authentication prompt.
|
|
86
86
|
const signature = await DeviceCrypto.sign(alias, payload, {
|
|
87
87
|
algorithmType,
|
|
88
88
|
authMethod, // Android: defined when signing
|
|
89
89
|
});
|
|
90
90
|
|
|
91
|
-
//
|
|
91
|
+
// 5) Verify locally (usually done server-side with stored public key)
|
|
92
92
|
const isValid = await DeviceCrypto.verify(alias, payload, signature ?? "", {
|
|
93
93
|
algorithmType,
|
|
94
94
|
});
|
|
@@ -98,6 +98,7 @@ const isValid = await DeviceCrypto.verify(alias, payload, signature ?? "", {
|
|
|
98
98
|
|
|
99
99
|
```ts
|
|
100
100
|
import DeviceCrypto, {
|
|
101
|
+
AuthCheckResult,
|
|
101
102
|
AuthMethod,
|
|
102
103
|
EncryptionAlgorithm,
|
|
103
104
|
} from "expo-device-crypto";
|
|
@@ -107,26 +108,72 @@ const secret = "Sensitive token";
|
|
|
107
108
|
const algorithmType = EncryptionAlgorithm.RSA_2048_PKCS1;
|
|
108
109
|
const authMethod = AuthMethod.PASSCODE_OR_BIOMETRIC;
|
|
109
110
|
|
|
110
|
-
// 1)
|
|
111
|
+
// 1) Ensure device authentication is configured (passcode at minimum)
|
|
112
|
+
const authStatus = DeviceCrypto.isAuthCheckAvailable();
|
|
113
|
+
if (authStatus !== AuthCheckResult.AVAILABLE) {
|
|
114
|
+
throw new Error(`Authentication unavailable: ${authStatus}`);
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
// 2) Generate an auth-protected RSA key pair
|
|
111
118
|
await DeviceCrypto.generateKeyPair(alias, {
|
|
112
119
|
algorithmType,
|
|
113
120
|
requireAuthentication: true,
|
|
114
121
|
authMethod, // iOS: defined at key generation;
|
|
115
122
|
});
|
|
116
123
|
|
|
117
|
-
//
|
|
124
|
+
// 3) Optional: retrieve/share public key with backend
|
|
118
125
|
const publicKeyPem = DeviceCrypto.getPublicKey(alias, { format: "PEM" });
|
|
119
126
|
|
|
120
|
-
//
|
|
127
|
+
// 4) Encrypt with public key
|
|
121
128
|
const encrypted = await DeviceCrypto.encrypt(alias, secret, {
|
|
122
129
|
algorithmType,
|
|
123
130
|
});
|
|
124
131
|
|
|
125
|
-
//
|
|
126
|
-
// Note: Data to
|
|
132
|
+
// 5) Decrypt with private key (prompts biometric/passcode on protected keys)
|
|
133
|
+
// Note: Data to decrypt must be in Base64 format.
|
|
134
|
+
// Note: This function should display the system user authentication prompt.
|
|
127
135
|
const decrypted = await DeviceCrypto.decrypt(alias, encrypted ?? "", {
|
|
128
136
|
algorithmType,
|
|
129
137
|
authMethod, // Android: defined when decrypting
|
|
130
138
|
});
|
|
131
139
|
```
|
|
132
140
|
|
|
141
|
+
> ⚠️ Because iOS Keychain/Secure Enclave binds authentication policy to key creation, `authMethod` must be set in `generateKeyPair`. On Android Keystore, authentication is applied at key usage time, so `authMethod` is provided in operations like `sign` and `decrypt`.
|
|
142
|
+
|
|
143
|
+
### Methods
|
|
144
|
+
|
|
145
|
+
- `isAuthCheckAvailable(): AuthCheckResult`
|
|
146
|
+
- Returns device authentication availability (`AVAILABLE`, `NO_HARDWARE`, `UNAVAILABLE`).
|
|
147
|
+
|
|
148
|
+
- `generateKeyPair(alias: string, options?: GenerateKeyPairOptions): Promise<GenerateKeyPairResult>`
|
|
149
|
+
- Creates a key pair for `alias` if it does not exist.
|
|
150
|
+
- Defaults: `requireAuthentication = false`, `authMethod = PASSCODE_OR_BIOMETRIC`.
|
|
151
|
+
|
|
152
|
+
- `getPublicKey(alias: string, options?: GetPublicKeyOptions): string | null`
|
|
153
|
+
- Returns public key in PEM format (or `null` if alias not found).
|
|
154
|
+
- Defaults: `format = "PEM"`.
|
|
155
|
+
|
|
156
|
+
- `removeKeyPair(alias: string): boolean`
|
|
157
|
+
- Removes the key pair for the alias.
|
|
158
|
+
|
|
159
|
+
- `aliases(): string[]`
|
|
160
|
+
- Lists stored key aliases.
|
|
161
|
+
|
|
162
|
+
- `sign(alias: string, data: string, options?: SignOptions): Promise<string | null>`
|
|
163
|
+
- Signs UTF-8 `data` with private key.
|
|
164
|
+
- Defaults: `algorithmType = ECDSA_SECP256R1_SHA256`, `promptTitle = "Unlock"`, `promptSubtitle = "Enter your PIN to continue"`, `authMethod = PASSCODE_OR_BIOMETRIC`.
|
|
165
|
+
|
|
166
|
+
- `verify(alias: string, data: string, signature: string, options?: VerifyOptions): Promise<boolean | null>`
|
|
167
|
+
- Verifies signature for UTF-8 `data`.
|
|
168
|
+
- Signature must be in Base64 format.
|
|
169
|
+
- Default: `algorithmType = ECDSA_SECP256R1_SHA256`.
|
|
170
|
+
|
|
171
|
+
- `encrypt(alias: string, data: string, options?: EncryptOptions): Promise<string | null>`
|
|
172
|
+
- Encrypts UTF-8 `data` with public key.
|
|
173
|
+
- Default: `algorithmType = RSA_2048_PKCS1`.
|
|
174
|
+
|
|
175
|
+
- `decrypt(alias: string, data: string, options?: DecryptOptions): Promise<string | null>`
|
|
176
|
+
- Decrypts Base64 `data` with private key.
|
|
177
|
+
- Defaults: `algorithmType = RSA_2048_PKCS1`, `promptTitle = "Unlock"`, `promptSubtitle = "Enter your PIN to continue"`, `authMethod = PASSCODE_OR_BIOMETRIC`.
|
|
178
|
+
|
|
179
|
+
> ✅ JSDoc type definitions are available in `./src/DeviceCryptoModule.ts`.
|
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.
|
|
4
|
+
version = '0.2.0'
|
|
5
5
|
|
|
6
6
|
def expoModulesCorePlugin = new File(project(":expo-modules-core").projectDir.absolutePath, "ExpoModulesCorePlugin.gradle")
|
|
7
7
|
apply from: expoModulesCorePlugin
|
|
@@ -35,7 +35,7 @@ android {
|
|
|
35
35
|
namespace "expo.modules.devicecrypto"
|
|
36
36
|
defaultConfig {
|
|
37
37
|
versionCode 1
|
|
38
|
-
versionName "0.
|
|
38
|
+
versionName "0.2.0"
|
|
39
39
|
}
|
|
40
40
|
lintOptions {
|
|
41
41
|
abortOnError false
|
|
@@ -1,26 +1,41 @@
|
|
|
1
1
|
package expo.modules.devicecrypto
|
|
2
2
|
|
|
3
|
+
import android.content.Context
|
|
3
4
|
import android.content.pm.PackageManager
|
|
4
5
|
import android.security.keystore.KeyGenParameterSpec
|
|
5
6
|
import android.security.keystore.KeyInfo
|
|
6
|
-
import android.security.keystore.KeyProperties
|
|
7
|
-
import
|
|
8
|
-
import
|
|
9
|
-
import
|
|
10
|
-
import
|
|
7
|
+
import android.security.keystore.KeyProperties.AUTH_BIOMETRIC_STRONG
|
|
8
|
+
import android.security.keystore.KeyProperties.AUTH_DEVICE_CREDENTIAL
|
|
9
|
+
import android.security.keystore.KeyProperties.DIGEST_SHA1
|
|
10
|
+
import android.security.keystore.KeyProperties.DIGEST_SHA256
|
|
11
|
+
import android.security.keystore.KeyProperties.ENCRYPTION_PADDING_RSA_OAEP
|
|
12
|
+
import android.security.keystore.KeyProperties.ENCRYPTION_PADDING_RSA_PKCS1
|
|
13
|
+
import android.security.keystore.KeyProperties.KEY_ALGORITHM_EC
|
|
14
|
+
import android.security.keystore.KeyProperties.KEY_ALGORITHM_RSA
|
|
15
|
+
import android.security.keystore.KeyProperties.PURPOSE_DECRYPT
|
|
16
|
+
import android.security.keystore.KeyProperties.PURPOSE_ENCRYPT
|
|
17
|
+
import android.security.keystore.KeyProperties.PURPOSE_SIGN
|
|
18
|
+
import android.security.keystore.KeyProperties.PURPOSE_VERIFY
|
|
11
19
|
import android.util.Base64
|
|
12
20
|
import androidx.biometric.BiometricManager
|
|
13
|
-
import androidx.biometric.BiometricPrompt
|
|
14
21
|
import androidx.biometric.BiometricManager.Authenticators.BIOMETRIC_STRONG
|
|
15
22
|
import androidx.biometric.BiometricManager.Authenticators.DEVICE_CREDENTIAL
|
|
23
|
+
import androidx.biometric.BiometricPrompt
|
|
16
24
|
import androidx.core.content.ContextCompat
|
|
17
25
|
import androidx.fragment.app.FragmentActivity
|
|
18
26
|
import expo.modules.kotlin.Promise
|
|
19
|
-
import
|
|
20
|
-
import
|
|
21
|
-
import android.content.Context
|
|
27
|
+
import expo.modules.kotlin.modules.Module
|
|
28
|
+
import expo.modules.kotlin.modules.ModuleDefinition
|
|
22
29
|
import java.security.KeyFactory
|
|
30
|
+
import java.security.KeyPairGenerator
|
|
31
|
+
import java.security.KeyStore
|
|
32
|
+
import java.security.Signature
|
|
33
|
+
import java.security.spec.AlgorithmParameterSpec
|
|
34
|
+
import java.security.spec.MGF1ParameterSpec
|
|
23
35
|
import javax.crypto.Cipher
|
|
36
|
+
import javax.crypto.spec.OAEPParameterSpec
|
|
37
|
+
import javax.crypto.spec.PSource
|
|
38
|
+
|
|
24
39
|
|
|
25
40
|
enum class GenerateKeyPairResult {
|
|
26
41
|
KEY_PAIR_GENERATED,
|
|
@@ -42,6 +57,7 @@ enum class AuthMethod {
|
|
|
42
57
|
enum class AlgorithmType {
|
|
43
58
|
ECDSA_SECP256R1_SHA256,
|
|
44
59
|
RSA_2048_PKCS1,
|
|
60
|
+
RSA_2048_OAEP_SHA1,
|
|
45
61
|
}
|
|
46
62
|
|
|
47
63
|
class DeviceCryptoModule : Module() {
|
|
@@ -49,6 +65,19 @@ class DeviceCryptoModule : Module() {
|
|
|
49
65
|
return when (algorithm) {
|
|
50
66
|
AlgorithmType.ECDSA_SECP256R1_SHA256 -> "SHA256withECDSA"
|
|
51
67
|
AlgorithmType.RSA_2048_PKCS1 -> "RSA/ECB/PKCS1Padding"
|
|
68
|
+
AlgorithmType.RSA_2048_OAEP_SHA1 -> "RSA/ECB/OAEPwithSHA-1AndMGF1Padding"
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
private fun getOaepSpec(algoType: AlgorithmType): AlgorithmParameterSpec? {
|
|
73
|
+
return when (algoType) {
|
|
74
|
+
AlgorithmType.RSA_2048_OAEP_SHA1 -> OAEPParameterSpec(
|
|
75
|
+
"SHA-1",
|
|
76
|
+
"MGF1",
|
|
77
|
+
MGF1ParameterSpec.SHA1,
|
|
78
|
+
PSource.PSpecified.DEFAULT
|
|
79
|
+
)
|
|
80
|
+
else -> null
|
|
52
81
|
}
|
|
53
82
|
}
|
|
54
83
|
|
|
@@ -108,23 +137,26 @@ class DeviceCryptoModule : Module() {
|
|
|
108
137
|
return true
|
|
109
138
|
}
|
|
110
139
|
|
|
111
|
-
private fun
|
|
140
|
+
private fun buildECDSA(
|
|
141
|
+
alias: String,
|
|
142
|
+
digest: String,
|
|
143
|
+
reqAuth: Boolean): KeyPairGenerator {
|
|
112
144
|
val kpg: KeyPairGenerator = KeyPairGenerator.getInstance(
|
|
113
|
-
|
|
145
|
+
KEY_ALGORITHM_EC,
|
|
114
146
|
"AndroidKeyStore"
|
|
115
147
|
)
|
|
116
148
|
|
|
117
149
|
val parameterSpec: KeyGenParameterSpec = KeyGenParameterSpec.Builder(
|
|
118
150
|
alias,
|
|
119
|
-
|
|
151
|
+
PURPOSE_SIGN or PURPOSE_VERIFY
|
|
120
152
|
).run {
|
|
121
|
-
setDigests(
|
|
153
|
+
setDigests(digest)
|
|
122
154
|
setIsStrongBoxBacked(false) // TODO: Prefer strong box if available
|
|
123
155
|
setUserAuthenticationRequired(reqAuth)
|
|
124
156
|
if (reqAuth) {
|
|
125
157
|
setUserAuthenticationParameters(
|
|
126
158
|
30, // 30 seconds
|
|
127
|
-
|
|
159
|
+
AUTH_BIOMETRIC_STRONG or AUTH_DEVICE_CREDENTIAL
|
|
128
160
|
)
|
|
129
161
|
}
|
|
130
162
|
build()
|
|
@@ -133,23 +165,30 @@ class DeviceCryptoModule : Module() {
|
|
|
133
165
|
return kpg
|
|
134
166
|
}
|
|
135
167
|
|
|
136
|
-
private fun
|
|
168
|
+
private fun buildRSA(
|
|
169
|
+
alias: String,
|
|
170
|
+
digest: String?,
|
|
171
|
+
padding: String,
|
|
172
|
+
reqAuth: Boolean): KeyPairGenerator {
|
|
137
173
|
val kpg: KeyPairGenerator = KeyPairGenerator.getInstance(
|
|
138
|
-
|
|
174
|
+
KEY_ALGORITHM_RSA,
|
|
139
175
|
"AndroidKeyStore"
|
|
140
176
|
)
|
|
141
177
|
|
|
142
178
|
val parameterSpec: KeyGenParameterSpec = KeyGenParameterSpec.Builder(
|
|
143
179
|
alias,
|
|
144
|
-
|
|
180
|
+
PURPOSE_ENCRYPT or PURPOSE_DECRYPT
|
|
145
181
|
).run {
|
|
146
|
-
|
|
182
|
+
if (digest != null) {
|
|
183
|
+
setDigests(digest)
|
|
184
|
+
}
|
|
185
|
+
setEncryptionPaddings(padding)
|
|
147
186
|
setIsStrongBoxBacked(false) // TODO: Prefer strong box if available
|
|
148
187
|
setUserAuthenticationRequired(reqAuth)
|
|
149
188
|
if (reqAuth) {
|
|
150
189
|
setUserAuthenticationParameters(
|
|
151
190
|
30, // 30 seconds
|
|
152
|
-
|
|
191
|
+
AUTH_BIOMETRIC_STRONG or AUTH_DEVICE_CREDENTIAL
|
|
153
192
|
)
|
|
154
193
|
}
|
|
155
194
|
build()
|
|
@@ -200,8 +239,8 @@ class DeviceCryptoModule : Module() {
|
|
|
200
239
|
.setSubtitle(subtitle)
|
|
201
240
|
.setAllowedAuthenticators(
|
|
202
241
|
when (authMethod) {
|
|
203
|
-
AuthMethod.PASSCODE ->
|
|
204
|
-
AuthMethod.PASSCODE_OR_BIOMETRIC ->
|
|
242
|
+
AuthMethod.PASSCODE -> DEVICE_CREDENTIAL
|
|
243
|
+
AuthMethod.PASSCODE_OR_BIOMETRIC -> BIOMETRIC_STRONG or DEVICE_CREDENTIAL
|
|
205
244
|
}
|
|
206
245
|
)
|
|
207
246
|
.build()
|
|
@@ -231,8 +270,9 @@ class DeviceCryptoModule : Module() {
|
|
|
231
270
|
|
|
232
271
|
val algoType = AlgorithmType.valueOf(o["algoType"] as String)
|
|
233
272
|
val kpg = when (algoType) {
|
|
234
|
-
AlgorithmType.ECDSA_SECP256R1_SHA256 ->
|
|
235
|
-
AlgorithmType.RSA_2048_PKCS1 ->
|
|
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)
|
|
236
276
|
else -> throw Exception("INVALID_ALGORITHM_TYPE")
|
|
237
277
|
}
|
|
238
278
|
|
|
@@ -328,9 +368,14 @@ class DeviceCryptoModule : Module() {
|
|
|
328
368
|
|
|
329
369
|
val algoType = AlgorithmType.valueOf(o["algoType"] as String)
|
|
330
370
|
val algo = toAndroidAlgo(algoType)
|
|
371
|
+
val oaepSpec = getOaepSpec(algoType)
|
|
331
372
|
|
|
332
373
|
val cipher = Cipher.getInstance(algo)
|
|
333
|
-
|
|
374
|
+
if (oaepSpec != null) {
|
|
375
|
+
cipher.init(Cipher.ENCRYPT_MODE, entry.certificate.publicKey, oaepSpec)
|
|
376
|
+
} else {
|
|
377
|
+
cipher.init(Cipher.ENCRYPT_MODE, entry.certificate.publicKey)
|
|
378
|
+
}
|
|
334
379
|
val encrypted: ByteArray = cipher.doFinal(data.toByteArray(Charsets.UTF_8))
|
|
335
380
|
promise.resolve(Base64.encodeToString(encrypted, Base64.NO_WRAP))
|
|
336
381
|
}
|
|
@@ -352,19 +397,28 @@ class DeviceCryptoModule : Module() {
|
|
|
352
397
|
val authMethod = o["authMethod"] as String
|
|
353
398
|
val algoType = AlgorithmType.valueOf(o["algoType"] as String)
|
|
354
399
|
val algo = toAndroidAlgo(algoType)
|
|
400
|
+
val oaepSpec = getOaepSpec(algoType)
|
|
355
401
|
|
|
356
402
|
val cipher = Cipher.getInstance(algo)
|
|
357
403
|
val ciphertext = Base64.decode(data, Base64.NO_WRAP)
|
|
358
404
|
|
|
359
405
|
if (!reqAuth) {
|
|
360
|
-
|
|
406
|
+
if (oaepSpec != null) {
|
|
407
|
+
cipher.init(Cipher.DECRYPT_MODE, entry.privateKey, oaepSpec)
|
|
408
|
+
} else {
|
|
409
|
+
cipher.init(Cipher.DECRYPT_MODE, entry.privateKey)
|
|
410
|
+
}
|
|
361
411
|
val decrypted: ByteArray = cipher.doFinal(ciphertext)
|
|
362
412
|
promise.resolve(String(decrypted, Charsets.UTF_8))
|
|
363
413
|
return@AsyncFunction
|
|
364
414
|
} else {
|
|
365
415
|
showAuthPrompt(
|
|
366
416
|
onSuccess = {
|
|
367
|
-
|
|
417
|
+
if (oaepSpec != null) {
|
|
418
|
+
cipher.init(Cipher.DECRYPT_MODE, entry.privateKey, oaepSpec)
|
|
419
|
+
} else {
|
|
420
|
+
cipher.init(Cipher.DECRYPT_MODE, entry.privateKey)
|
|
421
|
+
}
|
|
368
422
|
val decrypted: ByteArray = cipher.doFinal(ciphertext)
|
|
369
423
|
promise.resolve(String(decrypted, Charsets.UTF_8))
|
|
370
424
|
},
|
|
@@ -16,7 +16,8 @@ export declare enum SigningAlgorithm {
|
|
|
16
16
|
ECDSA_SECP256R1_SHA256 = "ECDSA_SECP256R1_SHA256"
|
|
17
17
|
}
|
|
18
18
|
export declare enum EncryptionAlgorithm {
|
|
19
|
-
RSA_2048_PKCS1 = "RSA_2048_PKCS1"
|
|
19
|
+
RSA_2048_PKCS1 = "RSA_2048_PKCS1",
|
|
20
|
+
RSA_2048_OAEP_SHA1 = "RSA_2048_OAEP_SHA1"
|
|
20
21
|
}
|
|
21
22
|
export interface GenerateKeyPairOptions {
|
|
22
23
|
/**
|
|
@@ -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;
|
|
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"}
|
|
@@ -22,5 +22,6 @@ export var SigningAlgorithm;
|
|
|
22
22
|
export var EncryptionAlgorithm;
|
|
23
23
|
(function (EncryptionAlgorithm) {
|
|
24
24
|
EncryptionAlgorithm["RSA_2048_PKCS1"] = "RSA_2048_PKCS1";
|
|
25
|
+
EncryptionAlgorithm["RSA_2048_OAEP_SHA1"] = "RSA_2048_OAEP_SHA1";
|
|
25
26
|
})(EncryptionAlgorithm || (EncryptionAlgorithm = {}));
|
|
26
27
|
//# sourceMappingURL=DeviceCrypto.types.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"DeviceCrypto.types.js","sourceRoot":"","sources":["../src/DeviceCrypto.types.ts"],"names":[],"mappings":"AAAA,MAAM,CAAN,IAAY,qBAIX;AAJD,WAAY,qBAAqB;IAC/B,kEAAyC,CAAA;IACzC,4EAAmD,CAAA;IACnD,wDAA+B,CAAA;AACjC,CAAC,EAJW,qBAAqB,KAArB,qBAAqB,QAIhC;AAED,MAAM,CAAN,IAAY,eAIX;AAJD,WAAY,eAAe;IACzB,0CAAuB,CAAA;IACvB,8CAA2B,CAAA;IAC3B,8CAA2B,CAAA;AAC7B,CAAC,EAJW,eAAe,KAAf,eAAe,QAI1B;AAED,MAAM,CAAN,IAAY,UAGX;AAHD,WAAY,UAAU;IACpB,mCAAqB,CAAA;IACrB,6DAA+C,CAAA;AACjD,CAAC,EAHW,UAAU,KAAV,UAAU,QAGrB;AAED,MAAM,CAAN,IAAY,gBAEX;AAFD,WAAY,gBAAgB;IAC1B,qEAAiD,CAAA;AACnD,CAAC,EAFW,gBAAgB,KAAhB,gBAAgB,QAE3B;AAED,MAAM,CAAN,IAAY,
|
|
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"]}
|
|
@@ -46,8 +46,8 @@ declare const _default: {
|
|
|
46
46
|
/**
|
|
47
47
|
* Verifies the given data with the signature for the given alias.
|
|
48
48
|
* @param alias - The alias to use for the key pair.
|
|
49
|
-
* @param data - The data to verify.
|
|
50
|
-
* @param signature - The signature to verify.
|
|
49
|
+
* @param data - The data to verify in UTF-8 format.
|
|
50
|
+
* @param signature - The signature to verify in Base64 format.
|
|
51
51
|
* @param options - The options for the operation.
|
|
52
52
|
* @default {
|
|
53
53
|
* algoType: SigningAlgorithm.ECDSA_SHA256,
|
|
@@ -82,5 +82,4 @@ declare const _default: {
|
|
|
82
82
|
decrypt: (alias: string, data: string, options?: DecryptOptions) => Promise<string | null>;
|
|
83
83
|
};
|
|
84
84
|
export default _default;
|
|
85
|
-
export type { GenerateKeyPairResult };
|
|
86
85
|
//# sourceMappingURL=DeviceCryptoModule.d.ts.map
|
|
@@ -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;;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"}
|
|
@@ -76,8 +76,8 @@ export default {
|
|
|
76
76
|
/**
|
|
77
77
|
* Verifies the given data with the signature for the given alias.
|
|
78
78
|
* @param alias - The alias to use for the key pair.
|
|
79
|
-
* @param data - The data to verify.
|
|
80
|
-
* @param signature - The signature to verify.
|
|
79
|
+
* @param data - The data to verify in UTF-8 format.
|
|
80
|
+
* @param signature - The signature to verify in Base64 format.
|
|
81
81
|
* @param options - The options for the operation.
|
|
82
82
|
* @default {
|
|
83
83
|
* algoType: SigningAlgorithm.ECDSA_SHA256,
|
|
@@ -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.\n * @param signature - The signature to verify.\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 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};"]}
|
package/ios/DeviceCrypto.podspec
CHANGED
package/ios/DeviceCrypto.swift
CHANGED
|
@@ -23,6 +23,7 @@ enum AuthMethod: String {
|
|
|
23
23
|
enum AlgorithmType: String {
|
|
24
24
|
case ECDSA_SECP256R1_SHA256 = "ECDSA_SECP256R1_SHA256"
|
|
25
25
|
case RSA_2048_PKCS1 = "RSA_2048_PKCS1"
|
|
26
|
+
case RSA_2048_OAEP_SHA1 = "RSA_2048_OAEP_SHA1"
|
|
26
27
|
}
|
|
27
28
|
|
|
28
29
|
public class DeviceCryptoModule: Module {
|
|
@@ -32,6 +33,8 @@ public class DeviceCryptoModule: Module {
|
|
|
32
33
|
return .ecdsaSignatureMessageX962SHA256
|
|
33
34
|
case .RSA_2048_PKCS1:
|
|
34
35
|
return .rsaEncryptionPKCS1
|
|
36
|
+
case .RSA_2048_OAEP_SHA1:
|
|
37
|
+
return .rsaEncryptionOAEPSHA1
|
|
35
38
|
}
|
|
36
39
|
}
|
|
37
40
|
|
|
@@ -186,7 +189,7 @@ public class DeviceCryptoModule: Module {
|
|
|
186
189
|
return privateStatus == errSecSuccess || publicStatus == errSecSuccess
|
|
187
190
|
}
|
|
188
191
|
|
|
189
|
-
private func
|
|
192
|
+
private func buildECDSA(alias: String, reqAuth: Bool, authMethod: AuthMethod) -> SecKey? {
|
|
190
193
|
let accessFlags: SecAccessControlCreateFlags
|
|
191
194
|
if reqAuth {
|
|
192
195
|
switch authMethod {
|
|
@@ -224,7 +227,7 @@ public class DeviceCryptoModule: Module {
|
|
|
224
227
|
return SecKeyCreateRandomKey(attributes, nil)
|
|
225
228
|
}
|
|
226
229
|
|
|
227
|
-
private func
|
|
230
|
+
private func buildRSA(alias: String, reqAuth: Bool, authMethod: AuthMethod) -> SecKey? {
|
|
228
231
|
let accessFlags: SecAccessControlCreateFlags
|
|
229
232
|
if reqAuth {
|
|
230
233
|
switch authMethod {
|
|
@@ -289,9 +292,11 @@ public class DeviceCryptoModule: Module {
|
|
|
289
292
|
|
|
290
293
|
switch algoType {
|
|
291
294
|
case .ECDSA_SECP256R1_SHA256:
|
|
292
|
-
self.
|
|
295
|
+
self.buildECDSA(alias: alias, reqAuth: reqAuth, authMethod: authMethod!)
|
|
293
296
|
case .RSA_2048_PKCS1:
|
|
294
|
-
self.
|
|
297
|
+
self.buildRSA(alias: alias, reqAuth: reqAuth, authMethod: authMethod!)
|
|
298
|
+
case .RSA_2048_OAEP_SHA1:
|
|
299
|
+
self.buildRSA(alias: alias, reqAuth: reqAuth, authMethod: authMethod!)
|
|
295
300
|
default:
|
|
296
301
|
throw NSError(
|
|
297
302
|
domain: "DeviceCrypto",
|
package/package.json
CHANGED
|
@@ -109,8 +109,8 @@ export default {
|
|
|
109
109
|
/**
|
|
110
110
|
* Verifies the given data with the signature for the given alias.
|
|
111
111
|
* @param alias - The alias to use for the key pair.
|
|
112
|
-
* @param data - The data to verify.
|
|
113
|
-
* @param signature - The signature to verify.
|
|
112
|
+
* @param data - The data to verify in UTF-8 format.
|
|
113
|
+
* @param signature - The signature to verify in Base64 format.
|
|
114
114
|
* @param options - The options for the operation.
|
|
115
115
|
* @default {
|
|
116
116
|
* algoType: SigningAlgorithm.ECDSA_SHA256,
|
|
@@ -166,6 +166,4 @@ export default {
|
|
|
166
166
|
};
|
|
167
167
|
return module.decrypt(alias, data, o);
|
|
168
168
|
},
|
|
169
|
-
};
|
|
170
|
-
|
|
171
|
-
export type { GenerateKeyPairResult };
|
|
169
|
+
};
|