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 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
- ### ✍️ Signature Algorithms
28
-
29
- - `ECDSA_SECP256R1_SHA256`
30
-
31
- **Curve:** P-256 / secp256r1. </br>
32
- **Hash:** SHA-256. </br>
33
-
34
- - `RSA_SHA256`
35
-
36
- **Key size:** 2048 bits. </br>
37
- **Padding:** PKCS#1 v1.5. </br>
38
- **Hash:** SHA-256. </br>
39
-
40
- - `RSA_SHA256_PSS`
41
-
42
- **Key size:** 2048 bits. </br>
43
- **Padding:** RSA-PSS (MGF1). </br>
44
- **Hash:** SHA-256. </br>
45
-
46
- ### 🔐 Encryption Algorithms
47
-
48
- - `RSA_2048_PKCS1`
49
-
50
- **Key size:** 2048 bits. </br>
51
- **Padding:** PKCS#1 v1.5. </br>
52
-
53
- - `RSA_2048_OAEP_SHA1`
54
-
55
- **Key size:** 2048 bits. </br>
56
- **Padding:** OAEP with SHA-1 and MGF1. </br>
57
-
58
- - `ECIES_P256_AES256_GCM`
59
-
60
- **Curve:** P-256 (secp256r1). </br>
61
- **Symmetric cipher:** AES-256-GCM. </br>
62
- **Key derivation:** HKDF-SHA256 (32-byte key from ECDH shared secret). </br>
63
-
64
- **More coming soon...**
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
- > ⚠️ 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`.
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
- **You can find additional usage examples in the [`examples` directory](https://github.com/piotr-pietras/expo-device-crypto/tree/master/examples) of the main repository.**
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
 
@@ -4,13 +4,13 @@ plugins {
4
4
  }
5
5
 
6
6
  group = 'expo.modules.devicecrypto'
7
- version = '1.0.1'
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.1"
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 -> OAEPParameterSpec(
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;CAClC;AAED,KAAK,cAAc,GAAG,uBAAuB,CAAC;AAC9C,KAAK,YAAY,GAAG,gBAAgB,GAAG,oBAAoB,CAAC;AAC5D,oBAAY,mBAAmB;IAC7B,cAAc,mBAAmB;IACjC,kBAAkB,uBAAuB;IACzC,qBAAqB,0BAA0B;CAChD;AAED,MAAM,MAAM,sBAAsB,GAAG;IACnC;;OAEG;IACH,aAAa,EAAE,gBAAgB,GAAG,mBAAmB,CAAC;IACtD;;;;;;;OAOG;IACH,qBAAqB,CAAC,EAAE,OAAO,CAAC;IAChC;;;;;;;;;;;;;;OAcG;IACH,UAAU,CAAC,EAAE,UAAU,CAAC;IACxB;;;;OAIG;IACH,eAAe,CAAC,EAAE,OAAO,CAAC;CAC3B,CAAA;AAED,KAAK,eAAe,GAAG;IACrB;;;;OAIG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB;;;;OAIG;IACH,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB;;;;;OAKG;IACH,UAAU,CAAC,EAAE,UAAU,CAAC;CACzB,CAAA;AAED,KAAK,kBAAkB,GAAG;IACxB;;OAEG;IACH,aAAa,EAAE,gBAAgB,CAAC;CACjC,CAAC;AACF,KAAK,iBAAiB,GAAG,kBAAkB,CAAC;AAE5C,KAAK,wBAAwB,GAAG;IAC9B;;OAEG;IACH,aAAa,EAAE,YAAY,CAAC;CAC7B,CAAC;AACF,KAAK,wBAAwB,GAAG,wBAAwB,CAAC;AAEzD,KAAK,0BAA0B,GAAG;IAChC;;OAEG;IACH,aAAa,EAAE,cAAc,CAAC;IAC9B;;OAEG;IACH,aAAa,EAAE,MAAM,CAAC;CACvB,CAAC;AACF,KAAK,0BAA0B,GAAG,0BAA0B,CAAC;AAE7D,MAAM,MAAM,WAAW,GAAG,eAAe,GAAG,kBAAkB,CAAC;AAC/D,MAAM,MAAM,aAAa,GAAG,iBAAiB,CAAC;AAE9C,MAAM,MAAM,cAAc,GACtB,CAAC,eAAe,GAAG,0BAA0B,CAAC,GAC9C,CAAC,eAAe,GAAG,wBAAwB,CAAC,CAAC;AACjD,MAAM,MAAM,cAAc,GACtB,CAAC,eAAe,GAAG,0BAA0B,CAAC,GAC9C,wBAAwB,CAAC;AAE7B,MAAM,WAAW,mBAAmB;IAClC;;;OAGG;IACH,MAAM,CAAC,EAAE,KAAK,GAAG,QAAQ,CAAC;CAC3B"}
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,gBAIX;AAJD,WAAY,gBAAgB;IAC1B,qEAAiD,CAAA;IACjD,6CAAyB,CAAA;IACzB,qDAAiC,CAAA;AACnC,CAAC,EAJW,gBAAgB,KAAhB,gBAAgB,QAI3B;AAID,MAAM,CAAN,IAAY,mBAIX;AAJD,WAAY,mBAAmB;IAC7B,wDAAiC,CAAA;IACjC,gEAAyC,CAAA;IACzC,sEAA+C,CAAA;AACjD,CAAC,EAJW,mBAAmB,KAAnB,mBAAmB,QAI9B","sourcesContent":["export enum GenerateKeyPairResult {\n KEY_PAIR_GENERATED = \"KEY_PAIR_GENERATED\",\n KEY_PAIR_ALREADY_EXISTS = \"KEY_PAIR_ALREADY_EXISTS\",\n NOT_AVAILABLE = \"NOT_AVAILABLE\",\n}\n\nexport enum AuthCheckResult {\n AVAILABLE = \"AVAILABLE\",\n NO_HARDWARE = \"NO_HARDWARE\",\n UNAVAILABLE = \"UNAVAILABLE\",\n}\n\nexport enum AuthMethod {\n PASSCODE = \"PASSCODE\",\n PASSCODE_OR_BIOMETRIC = \"PASSCODE_OR_BIOMETRIC\",\n}\n\nexport enum SigningAlgorithm {\n ECDSA_SECP256R1_SHA256 = \"ECDSA_SECP256R1_SHA256\",\n RSA_SHA256 = \"RSA_SHA256\",\n RSA_SHA256_PSS = \"RSA_SHA256_PSS\",\n}\n\ntype EciesAlgorithm = \"ECIES_P256_AES256_GCM\";\ntype RsaAlgorithm = \"RSA_2048_PKCS1\" | \"RSA_2048_OAEP_SHA1\";\nexport enum EncryptionAlgorithm {\n RSA_2048_PKCS1 = \"RSA_2048_PKCS1\",\n RSA_2048_OAEP_SHA1 = \"RSA_2048_OAEP_SHA1\",\n ECIES_P256_AES256_GCM = \"ECIES_P256_AES256_GCM\",\n}\n\nexport type GenerateKeyPairOptions = {\n /**\n * The algorithm type of the key to generate.\n */\n algorithmType: SigningAlgorithm | EncryptionAlgorithm;\n /**\n * Whether to require authentication to sign.\n * Setting this to true will prompt biometric or passcode authentication before signing.\n *\n * Please check the *`isAuthCheckAvailable`* function to see if authentication is available.\n * If authentication is not available, generating a key pair will throw an error.\n * @default false\n */\n requireAuthentication?: boolean;\n /**\n * The method of authentication to use.\n * Note that on Android you have to define it when signing 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"]}
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"]}
@@ -1,6 +1,6 @@
1
1
  Pod::Spec.new do |s|
2
2
  s.name = 'DeviceCrypto'
3
- s.version = '1.0.1'
3
+ s.version = '1.1.0'
4
4
  s.summary = 'Device crypto'
5
5
  s.description = 'Device crypto for Expo using device hardware capabilities'
6
6
  s.author = 'Piotr Pietras'
@@ -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: 2048,
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "expo-device-crypto",
3
- "version": "1.0.1",
3
+ "version": "1.1.0",
4
4
  "description": "Crypto for Expo using device hardware capabilities",
5
5
  "main": "build/index.js",
6
6
  "types": "build/index.d.ts",
@@ -19,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
  /**