react-native-dpop 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 CHANGED
@@ -10,13 +10,12 @@ React Native library for DPoP proof generation and key management.
10
10
  - Calculate JWK thumbprint (`SHA-256`, base64url).
11
11
  - Verify if a proof is bound to a given key alias.
12
12
  - Retrieve non-sensitive key metadata (hardware-backed, StrongBox info, etc.).
13
+ - iOS key storage uses Secure Enclave when available, with Keychain fallback.
13
14
 
14
15
  ## Platform Support
15
16
 
16
17
  - Android: supported.
17
- - iOS: planned.
18
-
19
- Current implementation throws on non-Android platforms.
18
+ - iOS: supported.
20
19
 
21
20
  ## Installation
22
21
 
@@ -24,6 +23,12 @@ Current implementation throws on non-Android platforms.
24
23
  npm install react-native-dpop
25
24
  ```
26
25
 
26
+ For iOS, install pods in your app project:
27
+
28
+ ```sh
29
+ cd ios && pod install
30
+ ```
31
+
27
32
  ## Quick Start
28
33
 
29
34
  ```ts
@@ -39,6 +44,7 @@ const dpop = await DPoP.generateProof({
39
44
  const proof = dpop.proof;
40
45
  const thumbprint = await dpop.calculateThumbprint();
41
46
  const publicJwk = await dpop.getPublicKey('JWK');
47
+ const isBound = await dpop.isBoundToAlias();
42
48
  ```
43
49
 
44
50
  ## API
@@ -78,6 +84,7 @@ const publicJwk = await dpop.getPublicKey('JWK');
78
84
  Native errors are rejected with codes such as:
79
85
 
80
86
  - `ERR_DPOP_GENERATE_PROOF`
87
+ - `ERR_DPOP_CALCULATE_THUMBPRINT`
81
88
  - `ERR_DPOP_PUBLIC_KEY`
82
89
  - `ERR_DPOP_SIGN_WITH_PRIVATE_KEY`
83
90
  - `ERR_DPOP_HAS_KEY_PAIR`
@@ -3,7 +3,8 @@ require "json"
3
3
  package = JSON.parse(File.read(File.join(__dir__, "package.json")))
4
4
 
5
5
  Pod::Spec.new do |s|
6
- s.name = "Dpop"
6
+ s.name = "ReactNativeDPoP"
7
+ s.module_name = "ReactNativeDPoP"
7
8
  s.version = package["version"]
8
9
  s.summary = package["description"]
9
10
  s.homepage = package["homepage"]
@@ -14,7 +15,10 @@ Pod::Spec.new do |s|
14
15
  s.source = { :git => "https://github.com/Cirilord/react-native-dpop.git", :tag => "#{s.version}" }
15
16
 
16
17
  s.source_files = "ios/**/*.{h,m,mm,swift,cpp}"
17
- s.private_header_files = "ios/**/*.h"
18
18
 
19
- install_modules_dependencies(s)
19
+ if respond_to?(:install_modules_dependencies, true)
20
+ install_modules_dependencies(s)
21
+ else
22
+ s.dependency "React-Core"
23
+ end
20
24
  end
@@ -1,5 +1,5 @@
1
1
  buildscript {
2
- ext.Dpop = [
2
+ ext.ReactNativeDPoP = [
3
3
  kotlinVersion: "2.0.21",
4
4
  minSdkVersion: 24,
5
5
  compileSdkVersion: 36,
@@ -11,7 +11,7 @@ buildscript {
11
11
  return rootProject.ext.get(prop)
12
12
  }
13
13
 
14
- return Dpop[prop]
14
+ return ReactNativeDPoP[prop]
15
15
  }
16
16
 
17
17
  repositories {
@@ -33,7 +33,7 @@ apply plugin: "kotlin-android"
33
33
  apply plugin: "com.facebook.react"
34
34
 
35
35
  android {
36
- namespace "com.dpop"
36
+ namespace "com.reactnativedpop"
37
37
 
38
38
  compileSdkVersion getExtOrDefault("compileSdkVersion")
39
39
 
@@ -1,4 +1,4 @@
1
- package com.dpop
1
+ package com.reactnativedpop
2
2
 
3
3
  import android.content.Context
4
4
  import android.content.pm.PackageManager
@@ -1,4 +1,4 @@
1
- package com.dpop
1
+ package com.reactnativedpop
2
2
 
3
3
  import com.facebook.react.bridge.Arguments
4
4
  import com.facebook.react.bridge.Promise
@@ -8,8 +8,8 @@ import java.security.Signature
8
8
  import java.util.UUID
9
9
  import org.json.JSONObject
10
10
 
11
- class DpopModule(reactContext: ReactApplicationContext) :
12
- NativeDpopSpec(reactContext) {
11
+ class DPoPModule(reactContext: ReactApplicationContext) :
12
+ NativeReactNativeDPoPSpec(reactContext) {
13
13
  private val keyStore = DPoPKeyStore(reactContext)
14
14
 
15
15
  private fun resolveAlias(alias: String?): String {
@@ -297,6 +297,6 @@ class DpopModule(reactContext: ReactApplicationContext) :
297
297
 
298
298
  companion object {
299
299
  private const val DEFAULT_ALIAS = "react-native-dpop"
300
- const val NAME = NativeDpopSpec.NAME
300
+ const val NAME = NativeReactNativeDPoPSpec.NAME
301
301
  }
302
302
  }
@@ -1,4 +1,4 @@
1
- package com.dpop
1
+ package com.reactnativedpop
2
2
 
3
3
  import com.facebook.react.BaseReactPackage
4
4
  import com.facebook.react.bridge.NativeModule
@@ -7,10 +7,10 @@ import com.facebook.react.module.model.ReactModuleInfo
7
7
  import com.facebook.react.module.model.ReactModuleInfoProvider
8
8
  import java.util.HashMap
9
9
 
10
- class DpopPackage : BaseReactPackage() {
10
+ class DPoPPackage : BaseReactPackage() {
11
11
  override fun getModule(name: String, reactContext: ReactApplicationContext): NativeModule? {
12
- return if (name == DpopModule.NAME) {
13
- DpopModule(reactContext)
12
+ return if (name == DPoPModule.NAME) {
13
+ DPoPModule(reactContext)
14
14
  } else {
15
15
  null
16
16
  }
@@ -18,9 +18,9 @@ class DpopPackage : BaseReactPackage() {
18
18
 
19
19
  override fun getReactModuleInfoProvider() = ReactModuleInfoProvider {
20
20
  mapOf(
21
- DpopModule.NAME to ReactModuleInfo(
22
- name = DpopModule.NAME,
23
- className = DpopModule.NAME,
21
+ DPoPModule.NAME to ReactModuleInfo(
22
+ name = DPoPModule.NAME,
23
+ className = DPoPModule.NAME,
24
24
  canOverrideExistingModule = false,
25
25
  needsEagerInit = false,
26
26
  isCxxModule = false,
@@ -1,4 +1,4 @@
1
- package com.dpop
1
+ package com.reactnativedpop
2
2
 
3
3
  import com.facebook.react.bridge.Arguments
4
4
  import com.facebook.react.bridge.ReadableArray
@@ -0,0 +1,99 @@
1
+ import Foundation
2
+ import Security
3
+
4
+ struct DPoPKeyPairReference {
5
+ let privateKey: SecKey
6
+ let publicKey: SecKey
7
+ }
8
+
9
+ struct DPoPKeyInfo {
10
+ let alias: String
11
+ let algorithm: String
12
+ let curve: String
13
+ let hasKeyPair: Bool
14
+ let insideSecureHardware: Bool
15
+ let strongBoxAvailable: Bool
16
+ let strongBoxBacked: Bool
17
+ }
18
+
19
+ final class DPoPKeyStore {
20
+ private let secureEnclave = SecureEnclaveKeyStore()
21
+ private let keychain = KeychainKeyStore()
22
+
23
+ func generateKeyPair(alias: String) throws {
24
+ try deleteKeyPair(alias: alias)
25
+
26
+ do {
27
+ try secureEnclave.generateKeyPair(alias: alias)
28
+ } catch {
29
+ try keychain.generateKeyPair(alias: alias)
30
+ }
31
+ }
32
+
33
+ func deleteKeyPair(alias: String) throws {
34
+ try secureEnclave.deleteKeyPair(alias: alias)
35
+ try keychain.deleteKeyPair(alias: alias)
36
+ }
37
+
38
+ func hasKeyPair(alias: String) -> Bool {
39
+ secureEnclave.hasKeyPair(alias: alias) || keychain.hasKeyPair(alias: alias)
40
+ }
41
+
42
+ func getKeyPair(alias: String) throws -> DPoPKeyPairReference {
43
+ if secureEnclave.hasKeyPair(alias: alias) {
44
+ return DPoPKeyPairReference(
45
+ privateKey: try secureEnclave.getPrivateKey(alias: alias),
46
+ publicKey: try secureEnclave.getPublicKey(alias: alias)
47
+ )
48
+ }
49
+
50
+ if keychain.hasKeyPair(alias: alias) {
51
+ return DPoPKeyPairReference(
52
+ privateKey: try keychain.getPrivateKey(alias: alias),
53
+ publicKey: try keychain.getPublicKey(alias: alias)
54
+ )
55
+ }
56
+
57
+ throw DPoPError.keyNotFound(alias: alias)
58
+ }
59
+
60
+ func getKeyInfo(alias: String) -> DPoPKeyInfo {
61
+ if secureEnclave.hasKeyPair(alias: alias) {
62
+ return DPoPKeyInfo(
63
+ alias: alias,
64
+ algorithm: "EC",
65
+ curve: "P-256",
66
+ hasKeyPair: true,
67
+ insideSecureHardware: true,
68
+ strongBoxAvailable: false,
69
+ strongBoxBacked: false
70
+ )
71
+ }
72
+
73
+ if keychain.hasKeyPair(alias: alias) {
74
+ return DPoPKeyInfo(
75
+ alias: alias,
76
+ algorithm: "EC",
77
+ curve: "P-256",
78
+ hasKeyPair: true,
79
+ insideSecureHardware: false,
80
+ strongBoxAvailable: false,
81
+ strongBoxBacked: false
82
+ )
83
+ }
84
+
85
+ return DPoPKeyInfo(
86
+ alias: alias,
87
+ algorithm: "EC",
88
+ curve: "P-256",
89
+ hasKeyPair: false,
90
+ insideSecureHardware: false,
91
+ strongBoxAvailable: false,
92
+ strongBoxBacked: false
93
+ )
94
+ }
95
+
96
+ func isHardwareBacked(alias: String) -> Bool {
97
+ secureEnclave.isHardwareBacked(alias: alias)
98
+ }
99
+ }
@@ -0,0 +1,5 @@
1
+ #import <React/RCTBridgeModule.h>
2
+
3
+ @interface ReactNativeDPoP : NSObject <RCTBridgeModule>
4
+
5
+ @end
@@ -0,0 +1,353 @@
1
+ import Foundation
2
+ import Security
3
+ import React
4
+
5
+ final class DPoPModule {
6
+ static let shared = DPoPModule()
7
+
8
+ private let keyStore = DPoPKeyStore()
9
+ private let defaultAlias = "react-native-dpop"
10
+
11
+ private init() {}
12
+
13
+ func resolveAlias(_ alias: String?) -> String {
14
+ guard let alias, !alias.isEmpty else {
15
+ return defaultAlias
16
+ }
17
+ return alias
18
+ }
19
+
20
+ func assertHardwareBacked(alias: String?) throws {
21
+ let effectiveAlias = resolveAlias(alias)
22
+ guard keyStore.hasKeyPair(alias: effectiveAlias) else {
23
+ throw DPoPError.keyNotFound(alias: effectiveAlias)
24
+ }
25
+
26
+ guard keyStore.isHardwareBacked(alias: effectiveAlias) else {
27
+ throw DPoPError.notHardwareBacked(alias: effectiveAlias)
28
+ }
29
+ }
30
+
31
+ func calculateThumbprint(alias: String?) throws -> String {
32
+ let effectiveAlias = resolveAlias(alias)
33
+ if !keyStore.hasKeyPair(alias: effectiveAlias) {
34
+ try keyStore.generateKeyPair(alias: effectiveAlias)
35
+ }
36
+ let keyPair = try keyStore.getKeyPair(alias: effectiveAlias)
37
+ let coordinates = try DPoPUtils.getPublicCoordinates(fromRawPublicKey: try DPoPUtils.toRawPublicKey(keyPair.publicKey))
38
+ return DPoPUtils.calculateThumbprint(kty: "EC", crv: "P-256", x: coordinates.x, y: coordinates.y)
39
+ }
40
+
41
+ func deleteKeyPair(alias: String?) throws {
42
+ try keyStore.deleteKeyPair(alias: resolveAlias(alias))
43
+ }
44
+
45
+ func getKeyInfo(alias: String?) -> [String: Any] {
46
+ let effectiveAlias = resolveAlias(alias)
47
+ let keyInfo = keyStore.getKeyInfo(alias: effectiveAlias)
48
+
49
+ if !keyInfo.hasKeyPair {
50
+ return [
51
+ "alias": effectiveAlias,
52
+ "hasKeyPair": false
53
+ ]
54
+ }
55
+
56
+ return [
57
+ "alias": keyInfo.alias,
58
+ "algorithm": keyInfo.algorithm,
59
+ "curve": keyInfo.curve,
60
+ "hasKeyPair": true,
61
+ "insideSecureHardware": keyInfo.insideSecureHardware,
62
+ "strongBoxAvailable": keyInfo.strongBoxAvailable,
63
+ "strongBoxBacked": keyInfo.strongBoxBacked
64
+ ]
65
+ }
66
+
67
+ func getPublicKeyDer(alias: String?) throws -> String {
68
+ let effectiveAlias = resolveAlias(alias)
69
+ if !keyStore.hasKeyPair(alias: effectiveAlias) {
70
+ try keyStore.generateKeyPair(alias: effectiveAlias)
71
+ }
72
+ let keyPair = try keyStore.getKeyPair(alias: effectiveAlias)
73
+ return DPoPUtils.base64UrlEncode(try DPoPUtils.toDerPublicKey(keyPair.publicKey))
74
+ }
75
+
76
+ func getPublicKeyJwk(alias: String?) throws -> [String: Any] {
77
+ let effectiveAlias = resolveAlias(alias)
78
+ if !keyStore.hasKeyPair(alias: effectiveAlias) {
79
+ try keyStore.generateKeyPair(alias: effectiveAlias)
80
+ }
81
+ let keyPair = try keyStore.getKeyPair(alias: effectiveAlias)
82
+ let coordinates = try DPoPUtils.getPublicCoordinates(fromRawPublicKey: try DPoPUtils.toRawPublicKey(keyPair.publicKey))
83
+ return [
84
+ "kty": "EC",
85
+ "crv": "P-256",
86
+ "x": coordinates.x,
87
+ "y": coordinates.y
88
+ ]
89
+ }
90
+
91
+ func getPublicKeyRaw(alias: String?) throws -> String {
92
+ let effectiveAlias = resolveAlias(alias)
93
+ if !keyStore.hasKeyPair(alias: effectiveAlias) {
94
+ try keyStore.generateKeyPair(alias: effectiveAlias)
95
+ }
96
+ let keyPair = try keyStore.getKeyPair(alias: effectiveAlias)
97
+ return DPoPUtils.base64UrlEncode(try DPoPUtils.toRawPublicKey(keyPair.publicKey))
98
+ }
99
+
100
+ func hasKeyPair(alias: String?) -> Bool {
101
+ keyStore.hasKeyPair(alias: resolveAlias(alias))
102
+ }
103
+
104
+ func isBoundToAlias(proof: String, alias: String?) throws -> Bool {
105
+ let effectiveAlias = resolveAlias(alias)
106
+ if !keyStore.hasKeyPair(alias: effectiveAlias) {
107
+ try keyStore.generateKeyPair(alias: effectiveAlias)
108
+ }
109
+ let keyPair = try keyStore.getKeyPair(alias: effectiveAlias)
110
+ return try DPoPUtils.isProofBoundToPublicKey(proof, publicKey: keyPair.publicKey)
111
+ }
112
+
113
+ func rotateKeyPair(alias: String?) throws {
114
+ try keyStore.generateKeyPair(alias: resolveAlias(alias))
115
+ }
116
+
117
+ func signWithDpopPrivateKey(payload: String, alias: String?) throws -> String {
118
+ let effectiveAlias = resolveAlias(alias)
119
+ if !keyStore.hasKeyPair(alias: effectiveAlias) {
120
+ try keyStore.generateKeyPair(alias: effectiveAlias)
121
+ }
122
+ let keyPair = try keyStore.getKeyPair(alias: effectiveAlias)
123
+ var error: Unmanaged<CFError>?
124
+ guard let derSignature = SecKeyCreateSignature(
125
+ keyPair.privateKey,
126
+ .ecdsaSignatureMessageX962SHA256,
127
+ Data(payload.utf8) as CFData,
128
+ &error
129
+ ) as Data? else {
130
+ throw DPoPError.securityError(error?.takeRetainedValue())
131
+ }
132
+ let joseSignature = try DPoPUtils.derToJose(derSignature, partLength: 32)
133
+ return DPoPUtils.base64UrlEncode(joseSignature)
134
+ }
135
+
136
+ func generateProof(
137
+ htu: String,
138
+ htm: String,
139
+ nonce: String?,
140
+ accessToken: String?,
141
+ additional: [String: Any]?,
142
+ kid: String?,
143
+ jti: String?,
144
+ iat: NSNumber?,
145
+ alias: String?
146
+ ) throws -> [String: Any] {
147
+ let effectiveAlias = resolveAlias(alias)
148
+ if !keyStore.hasKeyPair(alias: effectiveAlias) {
149
+ try keyStore.generateKeyPair(alias: effectiveAlias)
150
+ }
151
+ let keyPair = try keyStore.getKeyPair(alias: effectiveAlias)
152
+ let coordinates = try DPoPUtils.getPublicCoordinates(fromRawPublicKey: try DPoPUtils.toRawPublicKey(keyPair.publicKey))
153
+
154
+ var jwk: [String: Any] = [
155
+ "kty": "EC",
156
+ "crv": "P-256",
157
+ "x": coordinates.x,
158
+ "y": coordinates.y
159
+ ]
160
+
161
+ var header: [String: Any] = [
162
+ "typ": "dpop+jwt",
163
+ "alg": "ES256",
164
+ "jwk": jwk
165
+ ]
166
+
167
+ if let kid, !kid.isEmpty {
168
+ header["kid"] = kid
169
+ }
170
+
171
+ let issuedAt = iat?.int64Value ?? Int64(Date().timeIntervalSince1970)
172
+ let finalJti = (jti?.isEmpty == false) ? jti! : UUID().uuidString
173
+
174
+ var payload: [String: Any] = [
175
+ "jti": finalJti,
176
+ "htm": htm.uppercased(),
177
+ "htu": htu,
178
+ "iat": issuedAt
179
+ ]
180
+
181
+ if let nonce, !nonce.isEmpty {
182
+ payload["nonce"] = nonce
183
+ }
184
+
185
+ if let accessToken, !accessToken.isEmpty {
186
+ payload["ath"] = DPoPUtils.hashAccessToken(accessToken)
187
+ }
188
+
189
+ if let additional {
190
+ for (key, value) in additional {
191
+ payload[key] = value
192
+ }
193
+ }
194
+
195
+ let headerSegment = DPoPUtils.base64UrlEncode(try DPoPUtils.jsonData(header))
196
+ let payloadSegment = DPoPUtils.base64UrlEncode(try DPoPUtils.jsonData(payload))
197
+ let signingInput = "\(headerSegment).\(payloadSegment)"
198
+
199
+ var error: Unmanaged<CFError>?
200
+ guard let derSignature = SecKeyCreateSignature(
201
+ keyPair.privateKey,
202
+ .ecdsaSignatureMessageX962SHA256,
203
+ Data(signingInput.utf8) as CFData,
204
+ &error
205
+ ) as Data? else {
206
+ throw DPoPError.securityError(error?.takeRetainedValue())
207
+ }
208
+ let joseSignature = try DPoPUtils.derToJose(derSignature, partLength: 32)
209
+ let jwt = "\(signingInput).\(DPoPUtils.base64UrlEncode(joseSignature))"
210
+
211
+ let proofContext: [String: Any] = [
212
+ "htu": payload["htu"] as? String ?? htu,
213
+ "htm": payload["htm"] as? String ?? htm.uppercased(),
214
+ "nonce": payload["nonce"] ?? NSNull(),
215
+ "ath": payload["ath"] ?? NSNull(),
216
+ "kid": header["kid"] ?? NSNull(),
217
+ "jti": payload["jti"] as? String ?? finalJti,
218
+ "iat": Double(issuedAt),
219
+ "additional": additional ?? NSNull()
220
+ ]
221
+
222
+ return [
223
+ "proof": jwt,
224
+ "proofContext": proofContext
225
+ ]
226
+ }
227
+
228
+ }
229
+
230
+ @objc extension ReactNativeDPoP {
231
+ static func moduleName() -> String! {
232
+ "ReactNativeDPoP"
233
+ }
234
+
235
+ static func requiresMainQueueSetup() -> Bool {
236
+ false
237
+ }
238
+
239
+ func assertHardwareBacked(_ alias: String?, resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock) {
240
+ do {
241
+ try DPoPModule.shared.assertHardwareBacked(alias: alias)
242
+ resolve(nil)
243
+ } catch {
244
+ reject("ERR_DPOP_ASSERT_HARDWARE_BACKED", error.localizedDescription, error)
245
+ }
246
+ }
247
+
248
+ func calculateThumbprint(_ alias: String?, resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock) {
249
+ do {
250
+ resolve(try DPoPModule.shared.calculateThumbprint(alias: alias))
251
+ } catch {
252
+ reject("ERR_DPOP_CALCULATE_THUMBPRINT", error.localizedDescription, error)
253
+ }
254
+ }
255
+
256
+ func deleteKeyPair(_ alias: String?, resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock) {
257
+ do {
258
+ try DPoPModule.shared.deleteKeyPair(alias: alias)
259
+ resolve(nil)
260
+ } catch {
261
+ reject("ERR_DPOP_DELETE_KEY_PAIR", error.localizedDescription, error)
262
+ }
263
+ }
264
+
265
+ func getKeyInfo(_ alias: String?, resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock) {
266
+ resolve(DPoPModule.shared.getKeyInfo(alias: alias))
267
+ }
268
+
269
+ func getPublicKeyDer(_ alias: String?, resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock) {
270
+ do {
271
+ resolve(try DPoPModule.shared.getPublicKeyDer(alias: alias))
272
+ } catch {
273
+ reject("ERR_DPOP_PUBLIC_KEY", error.localizedDescription, error)
274
+ }
275
+ }
276
+
277
+ func getPublicKeyJwk(_ alias: String?, resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock) {
278
+ do {
279
+ resolve(try DPoPModule.shared.getPublicKeyJwk(alias: alias))
280
+ } catch {
281
+ reject("ERR_DPOP_PUBLIC_KEY", error.localizedDescription, error)
282
+ }
283
+ }
284
+
285
+ func getPublicKeyRaw(_ alias: String?, resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock) {
286
+ do {
287
+ resolve(try DPoPModule.shared.getPublicKeyRaw(alias: alias))
288
+ } catch {
289
+ reject("ERR_DPOP_PUBLIC_KEY", error.localizedDescription, error)
290
+ }
291
+ }
292
+
293
+ func hasKeyPair(_ alias: String?, resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock) {
294
+ resolve(DPoPModule.shared.hasKeyPair(alias: alias))
295
+ }
296
+
297
+ func isBoundToAlias(_ proof: String, alias: String?, resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock) {
298
+ do {
299
+ resolve(try DPoPModule.shared.isBoundToAlias(proof: proof, alias: alias))
300
+ } catch {
301
+ reject("ERR_DPOP_IS_BOUND_TO_ALIAS", error.localizedDescription, error)
302
+ }
303
+ }
304
+
305
+ func rotateKeyPair(_ alias: String?, resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock) {
306
+ do {
307
+ try DPoPModule.shared.rotateKeyPair(alias: alias)
308
+ resolve(nil)
309
+ } catch {
310
+ reject("ERR_DPOP_ROTATE_KEY_PAIR", error.localizedDescription, error)
311
+ }
312
+ }
313
+
314
+ func signWithDpopPrivateKey(_ payload: String, alias: String?, resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock) {
315
+ do {
316
+ resolve(try DPoPModule.shared.signWithDpopPrivateKey(payload: payload, alias: alias))
317
+ } catch {
318
+ reject("ERR_DPOP_SIGN_WITH_PRIVATE_KEY", error.localizedDescription, error)
319
+ }
320
+ }
321
+
322
+ func generateProof(
323
+ _ htu: String,
324
+ htm: String,
325
+ nonce: String?,
326
+ accessToken: String?,
327
+ additional: [String: Any]?,
328
+ kid: String?,
329
+ jti: String?,
330
+ iat: NSNumber?,
331
+ alias: String?,
332
+ resolve: @escaping RCTPromiseResolveBlock,
333
+ reject: @escaping RCTPromiseRejectBlock
334
+ ) {
335
+ do {
336
+ resolve(
337
+ try DPoPModule.shared.generateProof(
338
+ htu: htu,
339
+ htm: htm,
340
+ nonce: nonce,
341
+ accessToken: accessToken,
342
+ additional: additional,
343
+ kid: kid,
344
+ jti: jti,
345
+ iat: iat,
346
+ alias: alias
347
+ )
348
+ )
349
+ } catch {
350
+ reject("ERR_DPOP_GENERATE_PROOF", error.localizedDescription, error)
351
+ }
352
+ }
353
+ }