expo-device-crypto 1.0.1 → 1.1.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 +83 -42
- package/android/build.gradle +2 -2
- package/android/src/main/java/expo/modules/devicecrypto/DeviceCryptoModule.kt +61 -41
- package/android/src/main/java/expo/modules/devicecrypto/DeviceCryptoUtils.kt +40 -0
- package/build/DeviceCrypto.types.d.ts +7 -3
- package/build/DeviceCrypto.types.d.ts.map +1 -1
- package/build/DeviceCrypto.types.js +4 -0
- package/build/DeviceCrypto.types.js.map +1 -1
- package/ios/DeviceCrypto.podspec +1 -1
- package/ios/DeviceCrypto.swift +26 -85
- package/ios/DeviceCryptoUtils.swift +84 -0
- package/package.json +1 -1
- package/src/DeviceCrypto.types.ts +13 -4
package/README.md
CHANGED
|
@@ -24,44 +24,83 @@ If you want to allow Face ID on iOS, add this to your app config:
|
|
|
24
24
|
|
|
25
25
|
## Supported Algorithms
|
|
26
26
|
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
27
|
+
<table>
|
|
28
|
+
<thead>
|
|
29
|
+
<tr>
|
|
30
|
+
<th width="50%">✍️ Signature Algorithms</th>
|
|
31
|
+
<th width="50%">🔐 Encryption Algorithms</th>
|
|
32
|
+
</tr>
|
|
33
|
+
</thead>
|
|
34
|
+
<tbody>
|
|
35
|
+
<tr>
|
|
36
|
+
<td>
|
|
37
|
+
<code>ECDSA_SECP256R1_SHA256</code><br>
|
|
38
|
+
<strong>Curve:</strong> P-256 / secp256r1<br>
|
|
39
|
+
<strong>Hash:</strong> SHA-256
|
|
40
|
+
</td>
|
|
41
|
+
<td>
|
|
42
|
+
<code>ECIES_P256_AES256_GCM</code><br>
|
|
43
|
+
<strong>Curve:</strong> P-256 (secp256r1)<br>
|
|
44
|
+
<strong>Symmetric cipher:</strong> AES-256-GCM<br>
|
|
45
|
+
<strong>Key derivation:</strong> HKDF-SHA256 (32-byte key from ECDH shared secret)
|
|
46
|
+
<a href="https://github.com/piotr-pietras/expo-device-crypto/blob/master/examples/ecies.ts">see simple usage</a>
|
|
47
|
+
</td>
|
|
48
|
+
</tr>
|
|
49
|
+
<tr>
|
|
50
|
+
<td>
|
|
51
|
+
<code>RSA_SHA256</code><br>
|
|
52
|
+
<strong>Key size:</strong> 2048 bits<br>
|
|
53
|
+
<strong>Padding:</strong> PKCS#1 v1.5<br>
|
|
54
|
+
<strong>Hash:</strong> SHA-256
|
|
55
|
+
</td>
|
|
56
|
+
<td>
|
|
57
|
+
<code>RSA_2048_OAEP_SHA1</code><br>
|
|
58
|
+
<strong>Key size:</strong> 2048 bits<br>
|
|
59
|
+
<strong>Padding:</strong> OAEP with SHA-1 and MGF1
|
|
60
|
+
</td>
|
|
61
|
+
</tr>
|
|
62
|
+
<tr>
|
|
63
|
+
<td>
|
|
64
|
+
<code>RSA_SHA256_PSS</code><br>
|
|
65
|
+
<strong>Key size:</strong> 2048 bits<br>
|
|
66
|
+
<strong>Padding:</strong> RSA-PSS (MGF1)<br>
|
|
67
|
+
<strong>Hash:</strong> SHA-256
|
|
68
|
+
</td>
|
|
69
|
+
<td>
|
|
70
|
+
<code>RSA_2048_PKCS1</code><br>
|
|
71
|
+
<strong>Key size:</strong> 2048 bits<br>
|
|
72
|
+
<strong>Padding:</strong> PKCS#1 v1.5
|
|
73
|
+
</td>
|
|
74
|
+
</tr>
|
|
75
|
+
<tr>
|
|
76
|
+
<td>
|
|
77
|
+
<code>RSA_4096_SHA256</code><br>
|
|
78
|
+
<strong>Key size:</strong> 4096 bits<br>
|
|
79
|
+
<strong>Padding:</strong> PKCS#1 v1.5<br>
|
|
80
|
+
<strong>Hash:</strong> SHA-256
|
|
81
|
+
</td>
|
|
82
|
+
<td>
|
|
83
|
+
<code>RSA_4096_OAEP_SHA1</code><br>
|
|
84
|
+
<strong>Key size:</strong> 4096 bits<br>
|
|
85
|
+
<strong>Padding:</strong> OAEP with SHA-1 and MGF1
|
|
86
|
+
</td>
|
|
87
|
+
</tr>
|
|
88
|
+
<tr>
|
|
89
|
+
<td>
|
|
90
|
+
<code>RSA_4096_SHA256_PSS</code><br>
|
|
91
|
+
<strong>Key size:</strong> 4096 bits<br>
|
|
92
|
+
<strong>Padding:</strong> RSA-PSS (MGF1)<br>
|
|
93
|
+
<strong>Hash:</strong> SHA-256
|
|
94
|
+
</td>
|
|
95
|
+
<td>
|
|
96
|
+
<code>RSA_4096_PKCS1</code><br>
|
|
97
|
+
<strong>Key size:</strong> 4096 bits<br>
|
|
98
|
+
<strong>Padding:</strong> PKCS#1 v1.5
|
|
99
|
+
</td>
|
|
100
|
+
</tr>
|
|
101
|
+
|
|
102
|
+
</tbody>
|
|
103
|
+
</table>
|
|
65
104
|
|
|
66
105
|
## Example
|
|
67
106
|
|
|
@@ -109,11 +148,13 @@ const decrypted = await DeviceCrypto.decrypt(alias, encrypted, {
|
|
|
109
148
|
});
|
|
110
149
|
```
|
|
111
150
|
|
|
112
|
-
>
|
|
113
|
-
|
|
114
|
-
### 📚 More examples
|
|
151
|
+
> [!WARNING]
|
|
152
|
+
> 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`.
|
|
115
153
|
|
|
116
|
-
|
|
154
|
+
> [!TIP]
|
|
155
|
+
> ### 📚💡📦 More examples
|
|
156
|
+
>
|
|
157
|
+
> Looking for more examples? Browse the **[`examples` directory](https://github.com/piotr-pietras/expo-device-crypto/tree/master/examples)** for additional usage patterns (ECIES, signing, encryption, and more).
|
|
117
158
|
|
|
118
159
|
## Methods
|
|
119
160
|
|
package/android/build.gradle
CHANGED
|
@@ -4,13 +4,13 @@ plugins {
|
|
|
4
4
|
}
|
|
5
5
|
|
|
6
6
|
group = 'expo.modules.devicecrypto'
|
|
7
|
-
version = '1.0
|
|
7
|
+
version = '1.1.0'
|
|
8
8
|
|
|
9
9
|
android {
|
|
10
10
|
namespace "expo.modules.devicecrypto"
|
|
11
11
|
defaultConfig {
|
|
12
12
|
versionCode 1
|
|
13
|
-
versionName "1.0
|
|
13
|
+
versionName "1.1.0"
|
|
14
14
|
}
|
|
15
15
|
lintOptions {
|
|
16
16
|
abortOnError false
|
|
@@ -67,9 +67,13 @@ enum class AlgorithmType {
|
|
|
67
67
|
ECDSA_SECP256R1_SHA256,
|
|
68
68
|
RSA_2048_PKCS1,
|
|
69
69
|
RSA_2048_OAEP_SHA1,
|
|
70
|
+
RSA_4096_PKCS1,
|
|
71
|
+
RSA_4096_OAEP_SHA1,
|
|
70
72
|
ECIES_P256_AES256_GCM,
|
|
71
73
|
RSA_SHA256,
|
|
72
|
-
RSA_SHA256_PSS
|
|
74
|
+
RSA_SHA256_PSS,
|
|
75
|
+
RSA_4096_SHA256,
|
|
76
|
+
RSA_4096_SHA256_PSS
|
|
73
77
|
}
|
|
74
78
|
|
|
75
79
|
enum class Purpose {
|
|
@@ -82,52 +86,22 @@ class DeviceCryptoModule : Module() {
|
|
|
82
86
|
return when (algorithm) {
|
|
83
87
|
AlgorithmType.ECDSA_SECP256R1_SHA256 -> "SHA256withECDSA"
|
|
84
88
|
AlgorithmType.RSA_2048_PKCS1 -> "RSA/ECB/PKCS1Padding"
|
|
89
|
+
AlgorithmType.RSA_4096_PKCS1 -> "RSA/ECB/PKCS1Padding"
|
|
85
90
|
AlgorithmType.RSA_2048_OAEP_SHA1 -> "RSA/ECB/OAEPwithSHA-1AndMGF1Padding"
|
|
91
|
+
AlgorithmType.RSA_4096_OAEP_SHA1 -> "RSA/ECB/OAEPwithSHA-1AndMGF1Padding"
|
|
86
92
|
AlgorithmType.ECIES_P256_AES256_GCM -> "AES/GCM/NoPadding"
|
|
87
93
|
AlgorithmType.RSA_SHA256 -> "SHA256withRSA"
|
|
94
|
+
AlgorithmType.RSA_4096_SHA256 -> "SHA256withRSA"
|
|
88
95
|
AlgorithmType.RSA_SHA256_PSS -> "SHA256withRSA/PSS"
|
|
96
|
+
AlgorithmType.RSA_4096_SHA256_PSS -> "SHA256withRSA/PSS"
|
|
89
97
|
else -> throw Exception("INVALID_ALGORITHM_TYPE")
|
|
90
98
|
}
|
|
91
99
|
}
|
|
92
100
|
|
|
93
|
-
private fun hkdfSha256(
|
|
94
|
-
ikm: ByteArray,
|
|
95
|
-
length: Int,
|
|
96
|
-
salt: ByteArray? = null,
|
|
97
|
-
info: ByteArray? = null): ByteArray {
|
|
98
|
-
val mac = Mac.getInstance("HmacSHA256")
|
|
99
|
-
val actualSalt = salt ?: ByteArray(32)
|
|
100
|
-
mac.init(SecretKeySpec(actualSalt, "HmacSHA256"))
|
|
101
|
-
val prk = mac.doFinal(ikm)
|
|
102
|
-
|
|
103
|
-
val result = ByteArray(length)
|
|
104
|
-
var t = ByteArray(0)
|
|
105
|
-
var offset = 0
|
|
106
|
-
var counter = 1
|
|
107
|
-
|
|
108
|
-
while (offset < length) {
|
|
109
|
-
val macExpand = Mac.getInstance("HmacSHA256")
|
|
110
|
-
macExpand.init(SecretKeySpec(prk, "HmacSHA256"))
|
|
111
|
-
|
|
112
|
-
macExpand.update(t)
|
|
113
|
-
if (info != null) macExpand.update(info)
|
|
114
|
-
macExpand.update(counter.toByte())
|
|
115
|
-
|
|
116
|
-
t = macExpand.doFinal()
|
|
117
|
-
|
|
118
|
-
val toCopy = minOf(t.size, length - offset)
|
|
119
|
-
System.arraycopy(t, 0, result, offset, toCopy)
|
|
120
|
-
|
|
121
|
-
offset += toCopy
|
|
122
|
-
counter++
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
return result
|
|
126
|
-
}
|
|
127
|
-
|
|
128
101
|
private fun getOaepSpec(algoType: AlgorithmType): AlgorithmParameterSpec? {
|
|
129
102
|
return when (algoType) {
|
|
130
|
-
AlgorithmType.RSA_2048_OAEP_SHA1
|
|
103
|
+
AlgorithmType.RSA_2048_OAEP_SHA1,
|
|
104
|
+
AlgorithmType.RSA_4096_OAEP_SHA1 -> OAEPParameterSpec(
|
|
131
105
|
"SHA-1",
|
|
132
106
|
"MGF1",
|
|
133
107
|
MGF1ParameterSpec.SHA1,
|
|
@@ -197,6 +171,7 @@ class DeviceCryptoModule : Module() {
|
|
|
197
171
|
digest: String?,
|
|
198
172
|
purpose: Purpose,
|
|
199
173
|
padding: String?,
|
|
174
|
+
keySize: Int,
|
|
200
175
|
reqAuth: Boolean,
|
|
201
176
|
strongBox: Boolean): KeyPairGenerator {
|
|
202
177
|
val kpg: KeyPairGenerator = KeyPairGenerator.getInstance(
|
|
@@ -221,6 +196,7 @@ class DeviceCryptoModule : Module() {
|
|
|
221
196
|
setSignaturePaddings(padding)
|
|
222
197
|
}
|
|
223
198
|
setIsStrongBoxBacked(strongBox)
|
|
199
|
+
setKeySize(keySize)
|
|
224
200
|
setUserAuthenticationRequired(reqAuth)
|
|
225
201
|
if (reqAuth) {
|
|
226
202
|
setUserAuthenticationParameters(
|
|
@@ -355,6 +331,7 @@ class DeviceCryptoModule : Module() {
|
|
|
355
331
|
digest = null,
|
|
356
332
|
purpose = Purpose.ENCRYPT,
|
|
357
333
|
padding = ENCRYPTION_PADDING_RSA_PKCS1,
|
|
334
|
+
keySize = 2048,
|
|
358
335
|
reqAuth = reqAuth,
|
|
359
336
|
strongBox = strongBox
|
|
360
337
|
)
|
|
@@ -363,6 +340,25 @@ class DeviceCryptoModule : Module() {
|
|
|
363
340
|
digest = DIGEST_SHA1,
|
|
364
341
|
purpose = Purpose.ENCRYPT,
|
|
365
342
|
padding = ENCRYPTION_PADDING_RSA_OAEP,
|
|
343
|
+
keySize = 2048,
|
|
344
|
+
reqAuth = reqAuth,
|
|
345
|
+
strongBox = strongBox
|
|
346
|
+
)
|
|
347
|
+
AlgorithmType.RSA_4096_PKCS1 -> buildRSA(
|
|
348
|
+
alias = alias,
|
|
349
|
+
digest = null,
|
|
350
|
+
purpose = Purpose.ENCRYPT,
|
|
351
|
+
padding = ENCRYPTION_PADDING_RSA_PKCS1,
|
|
352
|
+
keySize = 4096,
|
|
353
|
+
reqAuth = reqAuth,
|
|
354
|
+
strongBox = strongBox
|
|
355
|
+
)
|
|
356
|
+
AlgorithmType.RSA_4096_OAEP_SHA1 -> buildRSA(
|
|
357
|
+
alias = alias,
|
|
358
|
+
digest = DIGEST_SHA1,
|
|
359
|
+
purpose = Purpose.ENCRYPT,
|
|
360
|
+
padding = ENCRYPTION_PADDING_RSA_OAEP,
|
|
361
|
+
keySize = 4096,
|
|
366
362
|
reqAuth = reqAuth,
|
|
367
363
|
strongBox = strongBox
|
|
368
364
|
)
|
|
@@ -377,6 +373,7 @@ class DeviceCryptoModule : Module() {
|
|
|
377
373
|
digest = DIGEST_SHA256,
|
|
378
374
|
purpose = Purpose.SIGN,
|
|
379
375
|
padding = SIGNATURE_PADDING_RSA_PKCS1,
|
|
376
|
+
keySize = 2048,
|
|
380
377
|
reqAuth = reqAuth,
|
|
381
378
|
strongBox = strongBox
|
|
382
379
|
)
|
|
@@ -385,6 +382,25 @@ class DeviceCryptoModule : Module() {
|
|
|
385
382
|
digest = DIGEST_SHA256,
|
|
386
383
|
purpose = Purpose.SIGN,
|
|
387
384
|
padding = SIGNATURE_PADDING_RSA_PSS,
|
|
385
|
+
keySize = 2048,
|
|
386
|
+
reqAuth = reqAuth,
|
|
387
|
+
strongBox = strongBox
|
|
388
|
+
)
|
|
389
|
+
AlgorithmType.RSA_4096_SHA256 -> buildRSA(
|
|
390
|
+
alias = alias,
|
|
391
|
+
digest = DIGEST_SHA256,
|
|
392
|
+
purpose = Purpose.SIGN,
|
|
393
|
+
padding = SIGNATURE_PADDING_RSA_PKCS1,
|
|
394
|
+
keySize = 4096,
|
|
395
|
+
reqAuth = reqAuth,
|
|
396
|
+
strongBox = strongBox
|
|
397
|
+
)
|
|
398
|
+
AlgorithmType.RSA_4096_SHA256_PSS -> buildRSA(
|
|
399
|
+
alias = alias,
|
|
400
|
+
digest = DIGEST_SHA256,
|
|
401
|
+
purpose = Purpose.SIGN,
|
|
402
|
+
padding = SIGNATURE_PADDING_RSA_PSS,
|
|
403
|
+
keySize = 4096,
|
|
388
404
|
reqAuth = reqAuth,
|
|
389
405
|
strongBox = strongBox
|
|
390
406
|
)
|
|
@@ -505,13 +521,15 @@ class DeviceCryptoModule : Module() {
|
|
|
505
521
|
|
|
506
522
|
fun runEncryption(cipher: Cipher) {
|
|
507
523
|
when (algoType) {
|
|
508
|
-
AlgorithmType.RSA_2048_OAEP_SHA1
|
|
524
|
+
AlgorithmType.RSA_2048_OAEP_SHA1,
|
|
525
|
+
AlgorithmType.RSA_4096_OAEP_SHA1 -> {
|
|
509
526
|
val oaepSpec = getOaepSpec(algoType)
|
|
510
527
|
cipher.init(Cipher.ENCRYPT_MODE, entry.certificate.publicKey, oaepSpec)
|
|
511
528
|
val encrypted = cipher.doFinal(data.toByteArray(Charsets.UTF_8))
|
|
512
529
|
promise.resolve(Base64.encodeToString(encrypted, Base64.NO_WRAP))
|
|
513
530
|
}
|
|
514
|
-
AlgorithmType.RSA_2048_PKCS1
|
|
531
|
+
AlgorithmType.RSA_2048_PKCS1,
|
|
532
|
+
AlgorithmType.RSA_4096_PKCS1 -> {
|
|
515
533
|
cipher.init(Cipher.ENCRYPT_MODE, entry.certificate.publicKey)
|
|
516
534
|
val encrypted = cipher.doFinal(data.toByteArray(Charsets.UTF_8))
|
|
517
535
|
promise.resolve(Base64.encodeToString(encrypted, Base64.NO_WRAP))
|
|
@@ -572,14 +590,16 @@ class DeviceCryptoModule : Module() {
|
|
|
572
590
|
|
|
573
591
|
fun runDecryption(cipher: Cipher) {
|
|
574
592
|
when (algoType) {
|
|
575
|
-
AlgorithmType.RSA_2048_OAEP_SHA1
|
|
593
|
+
AlgorithmType.RSA_2048_OAEP_SHA1,
|
|
594
|
+
AlgorithmType.RSA_4096_OAEP_SHA1 -> {
|
|
576
595
|
val oaepSpec = getOaepSpec(algoType)
|
|
577
596
|
cipher.init(Cipher.DECRYPT_MODE, entry.privateKey, oaepSpec)
|
|
578
597
|
val decrypted = cipher.doFinal(ciphertext)
|
|
579
598
|
promise.resolve(String(decrypted, Charsets.UTF_8))
|
|
580
599
|
return
|
|
581
600
|
}
|
|
582
|
-
AlgorithmType.RSA_2048_PKCS1
|
|
601
|
+
AlgorithmType.RSA_2048_PKCS1,
|
|
602
|
+
AlgorithmType.RSA_4096_PKCS1 -> {
|
|
583
603
|
cipher.init(Cipher.DECRYPT_MODE, entry.privateKey)
|
|
584
604
|
val decrypted = cipher.doFinal(ciphertext)
|
|
585
605
|
promise.resolve(String(decrypted, Charsets.UTF_8))
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
package expo.modules.devicecrypto
|
|
2
|
+
|
|
3
|
+
import javax.crypto.Mac
|
|
4
|
+
import javax.crypto.spec.SecretKeySpec
|
|
5
|
+
|
|
6
|
+
fun hkdfSha256(
|
|
7
|
+
ikm: ByteArray,
|
|
8
|
+
length: Int,
|
|
9
|
+
salt: ByteArray? = null,
|
|
10
|
+
info: ByteArray? = null
|
|
11
|
+
): ByteArray {
|
|
12
|
+
val mac = Mac.getInstance("HmacSHA256")
|
|
13
|
+
val actualSalt = salt ?: ByteArray(32)
|
|
14
|
+
mac.init(SecretKeySpec(actualSalt, "HmacSHA256"))
|
|
15
|
+
val prk = mac.doFinal(ikm)
|
|
16
|
+
|
|
17
|
+
val result = ByteArray(length)
|
|
18
|
+
var t = ByteArray(0)
|
|
19
|
+
var offset = 0
|
|
20
|
+
var counter = 1
|
|
21
|
+
|
|
22
|
+
while (offset < length) {
|
|
23
|
+
val macExpand = Mac.getInstance("HmacSHA256")
|
|
24
|
+
macExpand.init(SecretKeySpec(prk, "HmacSHA256"))
|
|
25
|
+
|
|
26
|
+
macExpand.update(t)
|
|
27
|
+
if (info != null) macExpand.update(info)
|
|
28
|
+
macExpand.update(counter.toByte())
|
|
29
|
+
|
|
30
|
+
t = macExpand.doFinal()
|
|
31
|
+
|
|
32
|
+
val toCopy = minOf(t.size, length - offset)
|
|
33
|
+
System.arraycopy(t, 0, result, offset, toCopy)
|
|
34
|
+
|
|
35
|
+
offset += toCopy
|
|
36
|
+
counter++
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
return result
|
|
40
|
+
}
|
|
@@ -15,15 +15,19 @@ export declare enum AuthMethod {
|
|
|
15
15
|
export declare enum SigningAlgorithm {
|
|
16
16
|
ECDSA_SECP256R1_SHA256 = "ECDSA_SECP256R1_SHA256",
|
|
17
17
|
RSA_SHA256 = "RSA_SHA256",
|
|
18
|
-
RSA_SHA256_PSS = "RSA_SHA256_PSS"
|
|
18
|
+
RSA_SHA256_PSS = "RSA_SHA256_PSS",
|
|
19
|
+
RSA_4096_SHA256 = "RSA_4096_SHA256",
|
|
20
|
+
RSA_4096_SHA256_PSS = "RSA_4096_SHA256_PSS"
|
|
19
21
|
}
|
|
20
|
-
type EciesAlgorithm = "ECIES_P256_AES256_GCM";
|
|
21
|
-
type RsaAlgorithm = "RSA_2048_PKCS1" | "RSA_2048_OAEP_SHA1";
|
|
22
22
|
export declare enum EncryptionAlgorithm {
|
|
23
23
|
RSA_2048_PKCS1 = "RSA_2048_PKCS1",
|
|
24
24
|
RSA_2048_OAEP_SHA1 = "RSA_2048_OAEP_SHA1",
|
|
25
|
+
RSA_4096_PKCS1 = "RSA_4096_PKCS1",
|
|
26
|
+
RSA_4096_OAEP_SHA1 = "RSA_4096_OAEP_SHA1",
|
|
25
27
|
ECIES_P256_AES256_GCM = "ECIES_P256_AES256_GCM"
|
|
26
28
|
}
|
|
29
|
+
type EciesAlgorithm = EncryptionAlgorithm.ECIES_P256_AES256_GCM;
|
|
30
|
+
type RsaAlgorithm = EncryptionAlgorithm.RSA_2048_PKCS1 | EncryptionAlgorithm.RSA_2048_OAEP_SHA1 | EncryptionAlgorithm.RSA_4096_PKCS1 | EncryptionAlgorithm.RSA_4096_OAEP_SHA1;
|
|
27
31
|
export type GenerateKeyPairOptions = {
|
|
28
32
|
/**
|
|
29
33
|
* The algorithm type of the key to generate.
|
|
@@ -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;IACjD,UAAU,eAAe;IACzB,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;IACjD,UAAU,eAAe;IACzB,cAAc,mBAAmB;IACjC,eAAe,oBAAoB;IACnC,mBAAmB,wBAAwB;CAC5C;AAED,oBAAY,mBAAmB;IAC7B,cAAc,mBAAmB;IACjC,kBAAkB,uBAAuB;IACzC,cAAc,mBAAmB;IACjC,kBAAkB,uBAAuB;IACzC,qBAAqB,0BAA0B;CAChD;AAED,KAAK,cAAc,GAAG,mBAAmB,CAAC,qBAAqB,CAAC;AAChE,KAAK,YAAY,GACb,mBAAmB,CAAC,cAAc,GAClC,mBAAmB,CAAC,kBAAkB,GACtC,mBAAmB,CAAC,cAAc,GAClC,mBAAmB,CAAC,kBAAkB,CAAC;AAE3C,MAAM,MAAM,sBAAsB,GAAG;IACnC;;OAEG;IACH,aAAa,EAAE,gBAAgB,GAAG,mBAAmB,CAAC;IACtD;;;;;;;OAOG;IACH,qBAAqB,CAAC,EAAE,OAAO,CAAC;IAChC;;;;;;;;;;;;;;OAcG;IACH,UAAU,CAAC,EAAE,UAAU,CAAC;IACxB;;;;OAIG;IACH,eAAe,CAAC,EAAE,OAAO,CAAC;CAC3B,CAAC;AAEF,KAAK,eAAe,GAAG;IACrB;;;;OAIG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB;;;;OAIG;IACH,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB;;;;;OAKG;IACH,UAAU,CAAC,EAAE,UAAU,CAAC;CACzB,CAAC;AAEF,KAAK,kBAAkB,GAAG;IACxB;;OAEG;IACH,aAAa,EAAE,gBAAgB,CAAC;CACjC,CAAC;AACF,KAAK,iBAAiB,GAAG,kBAAkB,CAAC;AAE5C,KAAK,wBAAwB,GAAG;IAC9B;;OAEG;IACH,aAAa,EAAE,YAAY,CAAC;CAC7B,CAAC;AACF,KAAK,wBAAwB,GAAG,wBAAwB,CAAC;AAEzD,KAAK,0BAA0B,GAAG;IAChC;;OAEG;IACH,aAAa,EAAE,cAAc,CAAC;IAC9B;;OAEG;IACH,aAAa,EAAE,MAAM,CAAC;CACvB,CAAC;AACF,KAAK,0BAA0B,GAAG,0BAA0B,CAAC;AAE7D,MAAM,MAAM,WAAW,GAAG,eAAe,GAAG,kBAAkB,CAAC;AAC/D,MAAM,MAAM,aAAa,GAAG,iBAAiB,CAAC;AAE9C,MAAM,MAAM,cAAc,GACtB,CAAC,eAAe,GAAG,0BAA0B,CAAC,GAC9C,CAAC,eAAe,GAAG,wBAAwB,CAAC,CAAC;AACjD,MAAM,MAAM,cAAc,GACtB,CAAC,eAAe,GAAG,0BAA0B,CAAC,GAC9C,wBAAwB,CAAC;AAE7B,MAAM,WAAW,mBAAmB;IAClC;;;OAGG;IACH,MAAM,CAAC,EAAE,KAAK,GAAG,QAAQ,CAAC;CAC3B"}
|
|
@@ -20,11 +20,15 @@ export var SigningAlgorithm;
|
|
|
20
20
|
SigningAlgorithm["ECDSA_SECP256R1_SHA256"] = "ECDSA_SECP256R1_SHA256";
|
|
21
21
|
SigningAlgorithm["RSA_SHA256"] = "RSA_SHA256";
|
|
22
22
|
SigningAlgorithm["RSA_SHA256_PSS"] = "RSA_SHA256_PSS";
|
|
23
|
+
SigningAlgorithm["RSA_4096_SHA256"] = "RSA_4096_SHA256";
|
|
24
|
+
SigningAlgorithm["RSA_4096_SHA256_PSS"] = "RSA_4096_SHA256_PSS";
|
|
23
25
|
})(SigningAlgorithm || (SigningAlgorithm = {}));
|
|
24
26
|
export var EncryptionAlgorithm;
|
|
25
27
|
(function (EncryptionAlgorithm) {
|
|
26
28
|
EncryptionAlgorithm["RSA_2048_PKCS1"] = "RSA_2048_PKCS1";
|
|
27
29
|
EncryptionAlgorithm["RSA_2048_OAEP_SHA1"] = "RSA_2048_OAEP_SHA1";
|
|
30
|
+
EncryptionAlgorithm["RSA_4096_PKCS1"] = "RSA_4096_PKCS1";
|
|
31
|
+
EncryptionAlgorithm["RSA_4096_OAEP_SHA1"] = "RSA_4096_OAEP_SHA1";
|
|
28
32
|
EncryptionAlgorithm["ECIES_P256_AES256_GCM"] = "ECIES_P256_AES256_GCM";
|
|
29
33
|
})(EncryptionAlgorithm || (EncryptionAlgorithm = {}));
|
|
30
34
|
//# 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,
|
|
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,gBAMX;AAND,WAAY,gBAAgB;IAC1B,qEAAiD,CAAA;IACjD,6CAAyB,CAAA;IACzB,qDAAiC,CAAA;IACjC,uDAAmC,CAAA;IACnC,+DAA2C,CAAA;AAC7C,CAAC,EANW,gBAAgB,KAAhB,gBAAgB,QAM3B;AAED,MAAM,CAAN,IAAY,mBAMX;AAND,WAAY,mBAAmB;IAC7B,wDAAiC,CAAA;IACjC,gEAAyC,CAAA;IACzC,wDAAiC,CAAA;IACjC,gEAAyC,CAAA;IACzC,sEAA+C,CAAA;AACjD,CAAC,EANW,mBAAmB,KAAnB,mBAAmB,QAM9B","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 RSA_SHA256 = \"RSA_SHA256\",\n RSA_SHA256_PSS = \"RSA_SHA256_PSS\",\n RSA_4096_SHA256 = \"RSA_4096_SHA256\",\n RSA_4096_SHA256_PSS = \"RSA_4096_SHA256_PSS\",\n}\n\nexport enum EncryptionAlgorithm {\n RSA_2048_PKCS1 = \"RSA_2048_PKCS1\",\n RSA_2048_OAEP_SHA1 = \"RSA_2048_OAEP_SHA1\",\n RSA_4096_PKCS1 = \"RSA_4096_PKCS1\",\n RSA_4096_OAEP_SHA1 = \"RSA_4096_OAEP_SHA1\",\n ECIES_P256_AES256_GCM = \"ECIES_P256_AES256_GCM\",\n}\n\ntype EciesAlgorithm = EncryptionAlgorithm.ECIES_P256_AES256_GCM;\ntype RsaAlgorithm =\n | EncryptionAlgorithm.RSA_2048_PKCS1\n | EncryptionAlgorithm.RSA_2048_OAEP_SHA1\n | EncryptionAlgorithm.RSA_4096_PKCS1\n | EncryptionAlgorithm.RSA_4096_OAEP_SHA1;\n\nexport type GenerateKeyPairOptions = {\n /**\n * The algorithm type of the key to generate.\n */\n algorithmType: SigningAlgorithm | EncryptionAlgorithm;\n /**\n * Whether to require authentication to sign.\n * Setting this to true will prompt biometric or passcode authentication before signing.\n *\n * Please check the *`isAuthCheckAvailable`* function to see if authentication is available.\n * If authentication is not available, generating a key pair will throw an error.\n * @default false\n */\n requireAuthentication?: boolean;\n /**\n * The method of authentication to use.\n * Note that on Android you have to define it when signing or decrypting.\n *\n * If you want to allow to use Face ID, you need to add the following to your app.json config file:\n * ```json\n * \"ios\": {\n * \"infoPlist\": {\n * \"NSFaceIDUsageDescription\": \"We use Face ID to secure your data.\",\n * }\n * }\n * ```\n * @default AuthMethod.PASSCODE_OR_BIOMETRIC\n * @platform ios 🍏\n */\n authMethod?: AuthMethod;\n /**\n * If the Strong Box is available, it will be used instead of the Trusted Execution Environment (TEE).\n * @default false\n * @platform android 🤖\n */\n preferStrongBox?: boolean;\n};\n\ntype BaseAuthOptions = {\n /**\n * The title of the prompt to show when authentication is required.\n * @default \"Unlock\"\n * @platform android 🤖\n */\n promptTitle?: string;\n /**\n * The subtitle of the prompt to show when authentication is required.\n * @default \"Enter your PIN to continue\"\n * @platform android 🤖\n */\n promptSubtitle?: string;\n /**\n * The method of authentication to use.\n * Note that on iOS you have to define it when generating the key pair.\n * @default AuthMethod.PASSCODE_OR_BIOMETRIC\n * @platform android 🤖\n */\n authMethod?: AuthMethod;\n};\n\ntype BaseSigningOptions = {\n /**\n * The algorithm type of the key to use for signing.\n */\n algorithmType: SigningAlgorithm;\n};\ntype BaseVerifyOptions = BaseSigningOptions;\n\ntype BaseRsaEncryptionOptions = {\n /**\n * The algorithm type of the key to use for encrypting.\n */\n algorithmType: RsaAlgorithm;\n};\ntype BaseRsaDecryptionOptions = BaseRsaEncryptionOptions;\n\ntype BaseEciesEncryptionOptions = {\n /**\n * The algorithm type of the key to use for encrypting.\n */\n algorithmType: EciesAlgorithm;\n /**\n * The peer public key to use for ECIES.\n */\n peerPublicKey: string;\n};\ntype BaseEciesDecryptionOptions = BaseEciesEncryptionOptions;\n\nexport type SignOptions = BaseAuthOptions & BaseSigningOptions;\nexport type VerifyOptions = BaseVerifyOptions;\n\nexport type DecryptOptions =\n | (BaseAuthOptions & BaseEciesDecryptionOptions)\n | (BaseAuthOptions & BaseRsaDecryptionOptions);\nexport type EncryptOptions =\n | (BaseAuthOptions & BaseEciesEncryptionOptions)\n | BaseRsaEncryptionOptions;\n\nexport interface GetPublicKeyOptions {\n /**\n * The format of the public key to return.\n * @default \"PEM\" (Base64 of PEM-encoded SubjectPublicKeyInfo (SPKI) for P‑256)\n */\n format?: \"PEM\" | \"BASE64\";\n}\n"]}
|
package/ios/DeviceCrypto.podspec
CHANGED
package/ios/DeviceCrypto.swift
CHANGED
|
@@ -25,9 +25,13 @@ enum AlgorithmType: String {
|
|
|
25
25
|
case ECDSA_SECP256R1_SHA256 = "ECDSA_SECP256R1_SHA256"
|
|
26
26
|
case RSA_2048_PKCS1 = "RSA_2048_PKCS1"
|
|
27
27
|
case RSA_2048_OAEP_SHA1 = "RSA_2048_OAEP_SHA1"
|
|
28
|
+
case RSA_4096_PKCS1 = "RSA_4096_PKCS1"
|
|
29
|
+
case RSA_4096_OAEP_SHA1 = "RSA_4096_OAEP_SHA1"
|
|
28
30
|
case ECIES_P256_AES256_GCM = "ECIES_P256_AES256_GCM"
|
|
29
31
|
case RSA_SHA256 = "RSA_SHA256"
|
|
30
32
|
case RSA_SHA256_PSS = "RSA_SHA256_PSS"
|
|
33
|
+
case RSA_4096_SHA256 = "RSA_4096_SHA256"
|
|
34
|
+
case RSA_4096_SHA256_PSS = "RSA_4096_SHA256_PSS"
|
|
31
35
|
}
|
|
32
36
|
|
|
33
37
|
public class DeviceCryptoModule: Module {
|
|
@@ -39,94 +43,23 @@ public class DeviceCryptoModule: Module {
|
|
|
39
43
|
return .rsaEncryptionPKCS1
|
|
40
44
|
case .RSA_2048_OAEP_SHA1:
|
|
41
45
|
return .rsaEncryptionOAEPSHA1
|
|
46
|
+
case .RSA_4096_PKCS1:
|
|
47
|
+
return .rsaEncryptionPKCS1
|
|
48
|
+
case .RSA_4096_OAEP_SHA1:
|
|
49
|
+
return .rsaEncryptionOAEPSHA1
|
|
42
50
|
case .ECIES_P256_AES256_GCM:
|
|
43
51
|
return .eciesEncryptionCofactorX963SHA256AESGCM
|
|
44
52
|
case .RSA_SHA256:
|
|
45
53
|
return .rsaSignatureMessagePKCS1v15SHA256
|
|
46
54
|
case .RSA_SHA256_PSS:
|
|
47
55
|
return .rsaSignatureMessagePSSSHA256
|
|
56
|
+
case .RSA_4096_SHA256:
|
|
57
|
+
return .rsaSignatureMessagePKCS1v15SHA256
|
|
58
|
+
case .RSA_4096_SHA256_PSS:
|
|
59
|
+
return .rsaSignatureMessagePSSSHA256
|
|
48
60
|
}
|
|
49
61
|
}
|
|
50
62
|
|
|
51
|
-
// Converts ANSI x962 EC point to P‑256 SPKI DER format
|
|
52
|
-
private func x962ECPointToP256SPKI(_ publicKey: SecKey) -> Data? {
|
|
53
|
-
var error: Unmanaged<CFError>?
|
|
54
|
-
guard let raw = SecKeyCopyExternalRepresentation(publicKey, &error) as Data? else {
|
|
55
|
-
return nil
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
guard raw.count == 65 else { return nil }
|
|
59
|
-
let prefix = Data([
|
|
60
|
-
0x30, 0x59,
|
|
61
|
-
0x30, 0x13,
|
|
62
|
-
0x06, 0x07, 0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x02, 0x01,
|
|
63
|
-
0x06, 0x08, 0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x03, 0x01, 0x07,
|
|
64
|
-
0x03, 0x42, 0x00
|
|
65
|
-
])
|
|
66
|
-
return prefix + raw
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
// Converts PKCS#1 RSA public key bytes to SPKI DER format.
|
|
70
|
-
private func rsaPKCS1ToSPKI(_ publicKey: SecKey) -> Data? {
|
|
71
|
-
guard let pkcs1 = SecKeyCopyExternalRepresentation(publicKey, nil) as Data? else {
|
|
72
|
-
return nil
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
// rsaEncryption OID: 1.2.840.113549.1.1.1 with NULL params
|
|
76
|
-
let algorithmIdentifier = Data([
|
|
77
|
-
0x30, 0x0D,
|
|
78
|
-
0x06, 0x09, 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x01,
|
|
79
|
-
0x05, 0x00
|
|
80
|
-
])
|
|
81
|
-
|
|
82
|
-
let bitStringPayload = Data([0x00]) + pkcs1
|
|
83
|
-
guard let bitString = asn1Wrap(tag: 0x03, content: bitStringPayload) else {
|
|
84
|
-
return nil
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
return asn1Wrap(tag: 0x30, content: algorithmIdentifier + bitString)
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
private func asn1Wrap(tag: UInt8, content: Data) -> Data? {
|
|
91
|
-
guard let length = asn1Length(content.count) else {
|
|
92
|
-
return nil
|
|
93
|
-
}
|
|
94
|
-
return Data([tag]) + length + content
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
private func asn1Length(_ length: Int) -> Data? {
|
|
98
|
-
if length < 0x80 {
|
|
99
|
-
return Data([UInt8(length)])
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
var value = length
|
|
103
|
-
var bytes: [UInt8] = []
|
|
104
|
-
while value > 0 {
|
|
105
|
-
bytes.insert(UInt8(value & 0xff), at: 0)
|
|
106
|
-
value >>= 8
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
guard bytes.count <= 4 else {
|
|
110
|
-
return nil
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
return Data([0x80 | UInt8(bytes.count)]) + Data(bytes)
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
// Converts P‑256 SPKI DER format to ANSI x962 EC point format.
|
|
117
|
-
private func spkiP256ToX962(_ spki: Data) -> Data? {
|
|
118
|
-
let prefix = Data([
|
|
119
|
-
0x30, 0x59,
|
|
120
|
-
0x30, 0x13,
|
|
121
|
-
0x06, 0x07, 0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x02, 0x01,
|
|
122
|
-
0x06, 0x08, 0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x03, 0x01, 0x07,
|
|
123
|
-
0x03, 0x42, 0x00
|
|
124
|
-
])
|
|
125
|
-
guard spki.count == prefix.count + 65 else { return nil }
|
|
126
|
-
guard spki.starts(with: prefix) else { return nil }
|
|
127
|
-
return spki.suffix(65)
|
|
128
|
-
}
|
|
129
|
-
|
|
130
63
|
private func getSecKeyQuery(_ alias: String, keyClass: CFString, returnRef: Bool = true) -> [String: Any] {
|
|
131
64
|
var query: [String: Any] = [
|
|
132
65
|
kSecClass as String: kSecClassKey,
|
|
@@ -185,7 +118,7 @@ public class DeviceCryptoModule: Module {
|
|
|
185
118
|
return SecKeyCreateRandomKey(attributes, nil)
|
|
186
119
|
}
|
|
187
120
|
|
|
188
|
-
private func buildRSA(alias: String, reqAuth: Bool, authMethod: AuthMethod) -> SecKey? {
|
|
121
|
+
private func buildRSA(alias: String, keySize: Int, reqAuth: Bool, authMethod: AuthMethod) -> SecKey? {
|
|
189
122
|
let accessFlags: SecAccessControlCreateFlags
|
|
190
123
|
if reqAuth {
|
|
191
124
|
switch authMethod {
|
|
@@ -207,7 +140,7 @@ public class DeviceCryptoModule: Module {
|
|
|
207
140
|
|
|
208
141
|
let attributes: NSDictionary = [
|
|
209
142
|
kSecAttrKeyType: kSecAttrKeyTypeRSA,
|
|
210
|
-
kSecAttrKeySizeInBits:
|
|
143
|
+
kSecAttrKeySizeInBits: keySize,
|
|
211
144
|
kSecPrivateKeyAttrs: [
|
|
212
145
|
kSecAttrIsPermanent: true,
|
|
213
146
|
kSecAttrApplicationTag: alias,
|
|
@@ -324,15 +257,23 @@ public class DeviceCryptoModule: Module {
|
|
|
324
257
|
case .ECDSA_SECP256R1_SHA256:
|
|
325
258
|
self.buildECDSA(alias: alias, reqAuth: reqAuth, authMethod: authMethod!)
|
|
326
259
|
case .RSA_2048_PKCS1:
|
|
327
|
-
self.buildRSA(alias: alias, reqAuth: reqAuth, authMethod: authMethod!)
|
|
260
|
+
self.buildRSA(alias: alias, keySize: 2048, reqAuth: reqAuth, authMethod: authMethod!)
|
|
328
261
|
case .RSA_2048_OAEP_SHA1:
|
|
329
|
-
self.buildRSA(alias: alias, reqAuth: reqAuth, authMethod: authMethod!)
|
|
262
|
+
self.buildRSA(alias: alias, keySize: 2048, reqAuth: reqAuth, authMethod: authMethod!)
|
|
263
|
+
case .RSA_4096_PKCS1:
|
|
264
|
+
self.buildRSA(alias: alias, keySize: 4096, reqAuth: reqAuth, authMethod: authMethod!)
|
|
265
|
+
case .RSA_4096_OAEP_SHA1:
|
|
266
|
+
self.buildRSA(alias: alias, keySize: 4096, reqAuth: reqAuth, authMethod: authMethod!)
|
|
330
267
|
case .ECIES_P256_AES256_GCM:
|
|
331
268
|
self.buildECIES(alias: alias, reqAuth: reqAuth, authMethod: authMethod!)
|
|
332
269
|
case .RSA_SHA256:
|
|
333
|
-
self.buildRSA(alias: alias, reqAuth: reqAuth, authMethod: authMethod!)
|
|
270
|
+
self.buildRSA(alias: alias, keySize: 2048, reqAuth: reqAuth, authMethod: authMethod!)
|
|
334
271
|
case .RSA_SHA256_PSS:
|
|
335
|
-
self.buildRSA(alias: alias, reqAuth: reqAuth, authMethod: authMethod!)
|
|
272
|
+
self.buildRSA(alias: alias, keySize: 2048, reqAuth: reqAuth, authMethod: authMethod!)
|
|
273
|
+
case .RSA_4096_SHA256:
|
|
274
|
+
self.buildRSA(alias: alias, keySize: 4096, reqAuth: reqAuth, authMethod: authMethod!)
|
|
275
|
+
case .RSA_4096_SHA256_PSS:
|
|
276
|
+
self.buildRSA(alias: alias, keySize: 4096, reqAuth: reqAuth, authMethod: authMethod!)
|
|
336
277
|
default:
|
|
337
278
|
throw NSError(
|
|
338
279
|
domain: "DeviceCrypto",
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import Foundation
|
|
2
|
+
import Security
|
|
3
|
+
|
|
4
|
+
/// Converts ANSI x9.62 EC point to P-256 SPKI DER format.
|
|
5
|
+
func x962ECPointToP256SPKI(_ publicKey: SecKey) -> Data? {
|
|
6
|
+
var error: Unmanaged<CFError>?
|
|
7
|
+
guard let raw = SecKeyCopyExternalRepresentation(publicKey, &error) as Data? else {
|
|
8
|
+
return nil
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
guard raw.count == 65 else { return nil }
|
|
12
|
+
let prefix = Data([
|
|
13
|
+
0x30, 0x59,
|
|
14
|
+
0x30, 0x13,
|
|
15
|
+
0x06, 0x07, 0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x02, 0x01,
|
|
16
|
+
0x06, 0x08, 0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x03, 0x01, 0x07,
|
|
17
|
+
0x03, 0x42, 0x00,
|
|
18
|
+
])
|
|
19
|
+
return prefix + raw
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/// Converts PKCS#1 RSA public key bytes to SPKI DER format.
|
|
23
|
+
func rsaPKCS1ToSPKI(_ publicKey: SecKey) -> Data? {
|
|
24
|
+
guard let pkcs1 = SecKeyCopyExternalRepresentation(publicKey, nil) as Data? else {
|
|
25
|
+
return nil
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
// rsaEncryption OID: 1.2.840.113549.1.1.1 with NULL params
|
|
29
|
+
let algorithmIdentifier = Data([
|
|
30
|
+
0x30, 0x0D,
|
|
31
|
+
0x06, 0x09, 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x01,
|
|
32
|
+
0x05, 0x00,
|
|
33
|
+
])
|
|
34
|
+
|
|
35
|
+
let bitStringPayload = Data([0x00]) + pkcs1
|
|
36
|
+
guard let bitString = asn1Wrap(tag: 0x03, content: bitStringPayload) else {
|
|
37
|
+
return nil
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
return asn1Wrap(tag: 0x30, content: algorithmIdentifier + bitString)
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/// Wraps ASN.1 content with a tag and length.
|
|
44
|
+
func asn1Wrap(tag: UInt8, content: Data) -> Data? {
|
|
45
|
+
guard let length = asn1Length(content.count) else {
|
|
46
|
+
return nil
|
|
47
|
+
}
|
|
48
|
+
return Data([tag]) + length + content
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/// Converts a length to ASN.1 length format.
|
|
52
|
+
func asn1Length(_ length: Int) -> Data? {
|
|
53
|
+
if length < 0x80 {
|
|
54
|
+
return Data([UInt8(length)])
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
var value = length
|
|
58
|
+
var bytes: [UInt8] = []
|
|
59
|
+
while value > 0 {
|
|
60
|
+
bytes.insert(UInt8(value & 0xff), at: 0)
|
|
61
|
+
value >>= 8
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
guard bytes.count <= 4 else {
|
|
65
|
+
return nil
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
return Data([0x80 | UInt8(bytes.count)]) + Data(bytes)
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/// Converts P-256 SPKI DER format to ANSI x9.62 EC point format.
|
|
72
|
+
func spkiP256ToX962(_ spki: Data) -> Data? {
|
|
73
|
+
let prefix = Data([
|
|
74
|
+
0x30, 0x59,
|
|
75
|
+
0x30, 0x13,
|
|
76
|
+
0x06, 0x07, 0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x02, 0x01,
|
|
77
|
+
0x06, 0x08, 0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x03, 0x01, 0x07,
|
|
78
|
+
0x03, 0x42, 0x00,
|
|
79
|
+
])
|
|
80
|
+
guard spki.count == prefix.count + 65 else { return nil }
|
|
81
|
+
guard spki.starts(with: prefix) else { return nil }
|
|
82
|
+
return spki.suffix(65)
|
|
83
|
+
}
|
|
84
|
+
|
package/package.json
CHANGED
|
@@ -19,16 +19,25 @@ export enum SigningAlgorithm {
|
|
|
19
19
|
ECDSA_SECP256R1_SHA256 = "ECDSA_SECP256R1_SHA256",
|
|
20
20
|
RSA_SHA256 = "RSA_SHA256",
|
|
21
21
|
RSA_SHA256_PSS = "RSA_SHA256_PSS",
|
|
22
|
+
RSA_4096_SHA256 = "RSA_4096_SHA256",
|
|
23
|
+
RSA_4096_SHA256_PSS = "RSA_4096_SHA256_PSS",
|
|
22
24
|
}
|
|
23
25
|
|
|
24
|
-
type EciesAlgorithm = "ECIES_P256_AES256_GCM";
|
|
25
|
-
type RsaAlgorithm = "RSA_2048_PKCS1" | "RSA_2048_OAEP_SHA1";
|
|
26
26
|
export enum EncryptionAlgorithm {
|
|
27
27
|
RSA_2048_PKCS1 = "RSA_2048_PKCS1",
|
|
28
28
|
RSA_2048_OAEP_SHA1 = "RSA_2048_OAEP_SHA1",
|
|
29
|
+
RSA_4096_PKCS1 = "RSA_4096_PKCS1",
|
|
30
|
+
RSA_4096_OAEP_SHA1 = "RSA_4096_OAEP_SHA1",
|
|
29
31
|
ECIES_P256_AES256_GCM = "ECIES_P256_AES256_GCM",
|
|
30
32
|
}
|
|
31
33
|
|
|
34
|
+
type EciesAlgorithm = EncryptionAlgorithm.ECIES_P256_AES256_GCM;
|
|
35
|
+
type RsaAlgorithm =
|
|
36
|
+
| EncryptionAlgorithm.RSA_2048_PKCS1
|
|
37
|
+
| EncryptionAlgorithm.RSA_2048_OAEP_SHA1
|
|
38
|
+
| EncryptionAlgorithm.RSA_4096_PKCS1
|
|
39
|
+
| EncryptionAlgorithm.RSA_4096_OAEP_SHA1;
|
|
40
|
+
|
|
32
41
|
export type GenerateKeyPairOptions = {
|
|
33
42
|
/**
|
|
34
43
|
* The algorithm type of the key to generate.
|
|
@@ -65,7 +74,7 @@ export type GenerateKeyPairOptions = {
|
|
|
65
74
|
* @platform android 🤖
|
|
66
75
|
*/
|
|
67
76
|
preferStrongBox?: boolean;
|
|
68
|
-
}
|
|
77
|
+
};
|
|
69
78
|
|
|
70
79
|
type BaseAuthOptions = {
|
|
71
80
|
/**
|
|
@@ -87,7 +96,7 @@ type BaseAuthOptions = {
|
|
|
87
96
|
* @platform android 🤖
|
|
88
97
|
*/
|
|
89
98
|
authMethod?: AuthMethod;
|
|
90
|
-
}
|
|
99
|
+
};
|
|
91
100
|
|
|
92
101
|
type BaseSigningOptions = {
|
|
93
102
|
/**
|