@doko/react-native-yubikit 0.0.2

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.
Files changed (144) hide show
  1. package/LICENSE +20 -0
  2. package/README.md +126 -0
  3. package/RNYubikit.podspec +22 -0
  4. package/android/build.gradle +63 -0
  5. package/android/src/main/AndroidManifest.xml +9 -0
  6. package/android/src/main/java/com/yubikit/YubiKitManagerHolder.kt +330 -0
  7. package/android/src/main/java/com/yubikit/YubikitCoreModule.kt +142 -0
  8. package/android/src/main/java/com/yubikit/YubikitFidoModule.kt +418 -0
  9. package/android/src/main/java/com/yubikit/YubikitManagementModule.kt +132 -0
  10. package/android/src/main/java/com/yubikit/YubikitOathModule.kt +224 -0
  11. package/android/src/main/java/com/yubikit/YubikitOpenPgpModule.kt +317 -0
  12. package/android/src/main/java/com/yubikit/YubikitPackage.kt +44 -0
  13. package/android/src/main/java/com/yubikit/YubikitPivModule.kt +310 -0
  14. package/android/src/main/java/com/yubikit/YubikitSupportModule.kt +108 -0
  15. package/android/src/main/java/com/yubikit/YubikitYubiOtpModule.kt +248 -0
  16. package/android/src/main/java/com/yubikit/utils/YubikitUtils.kt +321 -0
  17. package/ios/YubikitCore.h +5 -0
  18. package/ios/YubikitCore.mm +101 -0
  19. package/ios/YubikitDeviceInfoHelper.h +13 -0
  20. package/ios/YubikitDeviceInfoHelper.mm +110 -0
  21. package/ios/YubikitFido.h +5 -0
  22. package/ios/YubikitFido.mm +373 -0
  23. package/ios/YubikitManagement.h +5 -0
  24. package/ios/YubikitManagement.mm +153 -0
  25. package/ios/YubikitManager.h +27 -0
  26. package/ios/YubikitManager.m +169 -0
  27. package/ios/YubikitOath.h +5 -0
  28. package/ios/YubikitOath.mm +583 -0
  29. package/ios/YubikitOpenPgp.h +5 -0
  30. package/ios/YubikitOpenPgp.mm +210 -0
  31. package/ios/YubikitPiv.h +5 -0
  32. package/ios/YubikitPiv.mm +813 -0
  33. package/ios/YubikitSupport.h +5 -0
  34. package/ios/YubikitSupport.mm +58 -0
  35. package/ios/YubikitYubiOtp.h +5 -0
  36. package/ios/YubikitYubiOtp.mm +120 -0
  37. package/lib/module/core.js +34 -0
  38. package/lib/module/core.js.map +1 -0
  39. package/lib/module/fido.js +40 -0
  40. package/lib/module/fido.js.map +1 -0
  41. package/lib/module/hooks/index.js +5 -0
  42. package/lib/module/hooks/index.js.map +1 -0
  43. package/lib/module/hooks/use-yubikey.js +12 -0
  44. package/lib/module/hooks/use-yubikey.js.map +1 -0
  45. package/lib/module/hooks/yubikey-provider.js +85 -0
  46. package/lib/module/hooks/yubikey-provider.js.map +1 -0
  47. package/lib/module/index.js +12 -0
  48. package/lib/module/index.js.map +1 -0
  49. package/lib/module/management.js +17 -0
  50. package/lib/module/management.js.map +1 -0
  51. package/lib/module/oath.js +53 -0
  52. package/lib/module/oath.js.map +1 -0
  53. package/lib/module/openpgp.js +89 -0
  54. package/lib/module/openpgp.js.map +1 -0
  55. package/lib/module/package.json +1 -0
  56. package/lib/module/piv.js +71 -0
  57. package/lib/module/piv.js.map +1 -0
  58. package/lib/module/specs/NativeYubikitCore.js +10 -0
  59. package/lib/module/specs/NativeYubikitCore.js.map +1 -0
  60. package/lib/module/specs/NativeYubikitFido.js +5 -0
  61. package/lib/module/specs/NativeYubikitFido.js.map +1 -0
  62. package/lib/module/specs/NativeYubikitManagement.js +5 -0
  63. package/lib/module/specs/NativeYubikitManagement.js.map +1 -0
  64. package/lib/module/specs/NativeYubikitOath.js +5 -0
  65. package/lib/module/specs/NativeYubikitOath.js.map +1 -0
  66. package/lib/module/specs/NativeYubikitOpenPgp.js +5 -0
  67. package/lib/module/specs/NativeYubikitOpenPgp.js.map +1 -0
  68. package/lib/module/specs/NativeYubikitPiv.js +5 -0
  69. package/lib/module/specs/NativeYubikitPiv.js.map +1 -0
  70. package/lib/module/specs/NativeYubikitSupport.js +5 -0
  71. package/lib/module/specs/NativeYubikitSupport.js.map +1 -0
  72. package/lib/module/specs/NativeYubikitYubiOtp.js +5 -0
  73. package/lib/module/specs/NativeYubikitYubiOtp.js.map +1 -0
  74. package/lib/module/support.js +11 -0
  75. package/lib/module/support.js.map +1 -0
  76. package/lib/module/types.js +2 -0
  77. package/lib/module/types.js.map +1 -0
  78. package/lib/module/yubiotp.js +32 -0
  79. package/lib/module/yubiotp.js.map +1 -0
  80. package/lib/typescript/package.json +1 -0
  81. package/lib/typescript/src/core.d.ts +14 -0
  82. package/lib/typescript/src/core.d.ts.map +1 -0
  83. package/lib/typescript/src/fido.d.ts +24 -0
  84. package/lib/typescript/src/fido.d.ts.map +1 -0
  85. package/lib/typescript/src/hooks/index.d.ts +3 -0
  86. package/lib/typescript/src/hooks/index.d.ts.map +1 -0
  87. package/lib/typescript/src/hooks/use-yubikey.d.ts +3 -0
  88. package/lib/typescript/src/hooks/use-yubikey.d.ts.map +1 -0
  89. package/lib/typescript/src/hooks/yubikey-provider.d.ts +23 -0
  90. package/lib/typescript/src/hooks/yubikey-provider.d.ts.map +1 -0
  91. package/lib/typescript/src/index.d.ts +10 -0
  92. package/lib/typescript/src/index.d.ts.map +1 -0
  93. package/lib/typescript/src/management.d.ts +8 -0
  94. package/lib/typescript/src/management.d.ts.map +1 -0
  95. package/lib/typescript/src/oath.d.ts +20 -0
  96. package/lib/typescript/src/oath.d.ts.map +1 -0
  97. package/lib/typescript/src/openpgp.d.ts +35 -0
  98. package/lib/typescript/src/openpgp.d.ts.map +1 -0
  99. package/lib/typescript/src/piv.d.ts +26 -0
  100. package/lib/typescript/src/piv.d.ts.map +1 -0
  101. package/lib/typescript/src/specs/NativeYubikitCore.d.ts +49 -0
  102. package/lib/typescript/src/specs/NativeYubikitCore.d.ts.map +1 -0
  103. package/lib/typescript/src/specs/NativeYubikitFido.d.ts +121 -0
  104. package/lib/typescript/src/specs/NativeYubikitFido.d.ts.map +1 -0
  105. package/lib/typescript/src/specs/NativeYubikitManagement.d.ts +53 -0
  106. package/lib/typescript/src/specs/NativeYubikitManagement.d.ts.map +1 -0
  107. package/lib/typescript/src/specs/NativeYubikitOath.d.ts +70 -0
  108. package/lib/typescript/src/specs/NativeYubikitOath.d.ts.map +1 -0
  109. package/lib/typescript/src/specs/NativeYubikitOpenPgp.d.ts +53 -0
  110. package/lib/typescript/src/specs/NativeYubikitOpenPgp.d.ts.map +1 -0
  111. package/lib/typescript/src/specs/NativeYubikitPiv.d.ts +47 -0
  112. package/lib/typescript/src/specs/NativeYubikitPiv.d.ts.map +1 -0
  113. package/lib/typescript/src/specs/NativeYubikitSupport.d.ts +49 -0
  114. package/lib/typescript/src/specs/NativeYubikitSupport.d.ts.map +1 -0
  115. package/lib/typescript/src/specs/NativeYubikitYubiOtp.d.ts +45 -0
  116. package/lib/typescript/src/specs/NativeYubikitYubiOtp.d.ts.map +1 -0
  117. package/lib/typescript/src/support.d.ts +6 -0
  118. package/lib/typescript/src/support.d.ts.map +1 -0
  119. package/lib/typescript/src/types.d.ts +293 -0
  120. package/lib/typescript/src/types.d.ts.map +1 -0
  121. package/lib/typescript/src/yubiotp.d.ts +13 -0
  122. package/lib/typescript/src/yubiotp.d.ts.map +1 -0
  123. package/package.json +181 -0
  124. package/src/core.ts +58 -0
  125. package/src/fido.ts +112 -0
  126. package/src/hooks/index.tsx +2 -0
  127. package/src/hooks/use-yubikey.tsx +10 -0
  128. package/src/hooks/yubikey-provider.tsx +146 -0
  129. package/src/index.tsx +10 -0
  130. package/src/management.ts +44 -0
  131. package/src/oath.ts +134 -0
  132. package/src/openpgp.ts +220 -0
  133. package/src/piv.ts +193 -0
  134. package/src/specs/NativeYubikitCore.ts +68 -0
  135. package/src/specs/NativeYubikitFido.ts +149 -0
  136. package/src/specs/NativeYubikitManagement.ts +78 -0
  137. package/src/specs/NativeYubikitOath.ts +110 -0
  138. package/src/specs/NativeYubikitOpenPgp.ts +148 -0
  139. package/src/specs/NativeYubikitPiv.ts +108 -0
  140. package/src/specs/NativeYubikitSupport.ts +61 -0
  141. package/src/specs/NativeYubikitYubiOtp.ts +82 -0
  142. package/src/support.ts +18 -0
  143. package/src/types.ts +404 -0
  144. package/src/yubiotp.ts +96 -0
@@ -0,0 +1,583 @@
1
+ #import "YubikitOath.h"
2
+ #import "YubikitManager.h"
3
+ #import <YubiKit/YubiKit.h>
4
+
5
+ @implementation YubikitOath
6
+
7
+ #pragma mark - Helpers
8
+
9
+ - (id<YKFConnectionProtocol>)connectionForHandle:(NSString *)deviceHandle
10
+ reject:(RCTPromiseRejectBlock)reject {
11
+ id<YKFConnectionProtocol> connection = [[YubikitManager shared] connectionForHandle:deviceHandle];
12
+ if (connection == nil) {
13
+ reject(@"CONNECTION_ERROR", @"No device found for handle", nil);
14
+ }
15
+ return connection;
16
+ }
17
+
18
+ - (YKFOATHCredentialType)oathTypeFromString:(NSString *)type {
19
+ if ([type isEqualToString:@"HOTP"]) return YKFOATHCredentialTypeHOTP;
20
+ return YKFOATHCredentialTypeTOTP;
21
+ }
22
+
23
+ - (NSString *)stringFromOathType:(YKFOATHCredentialType)type {
24
+ return type == YKFOATHCredentialTypeHOTP ? @"HOTP" : @"TOTP";
25
+ }
26
+
27
+ - (YKFOATHCredentialAlgorithm)algorithmFromString:(NSString *)algorithm {
28
+ if ([algorithm isEqualToString:@"SHA256"]) return YKFOATHCredentialAlgorithmSHA256;
29
+ if ([algorithm isEqualToString:@"SHA512"]) return YKFOATHCredentialAlgorithmSHA512;
30
+ return YKFOATHCredentialAlgorithmSHA1;
31
+ }
32
+
33
+ - (NSString *)credentialIdFromCredential:(YKFOATHCredential *)credential {
34
+ NSDictionary *dict = @{
35
+ @"oathType": [self stringFromOathType:credential.type],
36
+ @"accountName": credential.accountName ?: @"",
37
+ @"issuer": credential.issuer ?: @"",
38
+ @"period": @(credential.period)
39
+ };
40
+ NSData *data = [NSJSONSerialization dataWithJSONObject:dict options:0 error:nil];
41
+ return [data base64EncodedStringWithOptions:0];
42
+ }
43
+
44
+ - (nullable YKFOATHCredential *)findCredentialWithId:(NSString *)credentialId
45
+ inCredentialList:(NSArray<YKFOATHCredential *> *)credentials {
46
+ NSData *data = [[NSData alloc] initWithBase64EncodedString:credentialId options:0];
47
+ if (data == nil) return nil;
48
+ NSDictionary *dict = [NSJSONSerialization JSONObjectWithData:data options:0 error:nil];
49
+ if (dict == nil) return nil;
50
+
51
+ NSString *type = dict[@"oathType"];
52
+ NSString *accountName = dict[@"accountName"];
53
+ NSString *issuer = dict[@"issuer"];
54
+ NSUInteger period = [dict[@"period"] unsignedIntegerValue];
55
+ YKFOATHCredentialType credentialType = [self oathTypeFromString:type];
56
+
57
+ for (YKFOATHCredential *credential in credentials) {
58
+ if (credential.type == credentialType &&
59
+ credential.period == period &&
60
+ [credential.accountName isEqualToString:accountName] &&
61
+ ((credential.issuer == nil && [issuer length] == 0) || [credential.issuer isEqualToString:issuer])) {
62
+ return credential;
63
+ }
64
+ }
65
+ return nil;
66
+ }
67
+
68
+ - (NSDictionary *)dictionaryFromCredential:(YKFOATHCredential *)credential {
69
+ return @{
70
+ @"id": [self credentialIdFromCredential:credential],
71
+ @"oathType": [self stringFromOathType:credential.type],
72
+ @"accountName": credential.accountName ?: @"",
73
+ @"issuer": credential.issuer ?: [NSNull null],
74
+ @"period": @(credential.period),
75
+ @"touchRequired": @(credential.requiresTouch)
76
+ };
77
+ }
78
+
79
+ - (NSDictionary *)dictionaryFromCode:(YKFOATHCode *)code {
80
+ NSTimeInterval validFrom = [code.validity.startDate timeIntervalSince1970] * 1000;
81
+ NSTimeInterval validUntil = [code.validity.endDate timeIntervalSince1970] * 1000;
82
+ return @{
83
+ @"value": code.otp ?: @"",
84
+ @"validFrom": @(validFrom),
85
+ @"validUntil": @(validUntil)
86
+ };
87
+ }
88
+
89
+ RCT_EXPORT_MODULE(YubikitOath)
90
+
91
+ - (void)getDeviceId:(NSString *)deviceHandle
92
+ resolve:(RCTPromiseResolveBlock)resolve
93
+ reject:(RCTPromiseRejectBlock)reject {
94
+ id<YKFConnectionProtocol> connection = [self connectionForHandle:deviceHandle reject:reject];
95
+ if (!connection) return;
96
+
97
+ [connection oathSession:^(YKFOATHSession *_Nullable session, NSError *_Nullable error) {
98
+ if (error || session == nil) {
99
+ reject(@"OATH_ERROR", error ? error.localizedDescription : @"OATH session not available", error);
100
+ return;
101
+ }
102
+ resolve(session.deviceId);
103
+ }];
104
+ }
105
+
106
+ - (void)reset:(NSString *)deviceHandle
107
+ resolve:(RCTPromiseResolveBlock)resolve
108
+ reject:(RCTPromiseRejectBlock)reject {
109
+ id<YKFConnectionProtocol> connection = [self connectionForHandle:deviceHandle reject:reject];
110
+ if (!connection) return;
111
+
112
+ [connection oathSession:^(YKFOATHSession *_Nullable session, NSError *_Nullable error) {
113
+ if (error || session == nil) {
114
+ reject(@"OATH_ERROR", error ? error.localizedDescription : @"OATH session not available", error);
115
+ return;
116
+ }
117
+ [session resetWithCompletion:^(NSError *_Nullable error) {
118
+ if (error) {
119
+ reject(@"OATH_ERROR", error.localizedDescription, error);
120
+ return;
121
+ }
122
+ resolve(nil);
123
+ }];
124
+ }];
125
+ }
126
+
127
+ - (void)isAccessKeySet:(NSString *)deviceHandle
128
+ resolve:(RCTPromiseResolveBlock)resolve
129
+ reject:(RCTPromiseRejectBlock)reject {
130
+ id<YKFConnectionProtocol> connection = [self connectionForHandle:deviceHandle reject:reject];
131
+ if (!connection) return;
132
+
133
+ [connection oathSession:^(YKFOATHSession *_Nullable session, NSError *_Nullable error) {
134
+ if (error || session == nil) {
135
+ reject(@"OATH_ERROR", error ? error.localizedDescription : @"OATH session not available", error);
136
+ return;
137
+ }
138
+ // The iOS SDK does not expose "is a password configured" directly. The only signal
139
+ // available through public API is YKFOATHErrorCodeAuthenticationRequired from a
140
+ // command that needs auth, which proves a password IS set - but a *lack* of that
141
+ // error only proves the session isn't currently locked, not that no password was
142
+ // ever configured (it may have already been unlocked earlier in this connection's
143
+ // lifetime; see unlockWithPassword's docs). So this can under-report true here.
144
+ [session listCredentialsWithCompletion:^(NSArray<YKFOATHCredential *> *_Nullable credentials, NSError *_Nullable error) {
145
+ if (error && [error isKindOfClass:[YKFOATHError class]] && error.code == YKFOATHErrorCodeAuthenticationRequired) {
146
+ resolve(@YES);
147
+ } else if (error) {
148
+ reject(@"OATH_ERROR", error.localizedDescription, error);
149
+ } else {
150
+ resolve(@NO);
151
+ }
152
+ }];
153
+ }];
154
+ }
155
+
156
+ - (void)isLocked:(NSString *)deviceHandle
157
+ resolve:(RCTPromiseResolveBlock)resolve
158
+ reject:(RCTPromiseRejectBlock)reject {
159
+ id<YKFConnectionProtocol> connection = [self connectionForHandle:deviceHandle reject:reject];
160
+ if (!connection) return;
161
+
162
+ [connection oathSession:^(YKFOATHSession *_Nullable session, NSError *_Nullable error) {
163
+ if (error || session == nil) {
164
+ reject(@"OATH_ERROR", error ? error.localizedDescription : @"OATH session not available", error);
165
+ return;
166
+ }
167
+ // Unlike isAccessKeySet, this only cares about the session's *current* auth state,
168
+ // which YKFOATHErrorCodeAuthenticationRequired answers precisely and reliably.
169
+ [session listCredentialsWithCompletion:^(NSArray<YKFOATHCredential *> *_Nullable credentials, NSError *_Nullable error) {
170
+ if (error && [error isKindOfClass:[YKFOATHError class]] && error.code == YKFOATHErrorCodeAuthenticationRequired) {
171
+ resolve(@YES);
172
+ } else if (error) {
173
+ reject(@"OATH_ERROR", error.localizedDescription, error);
174
+ } else {
175
+ resolve(@NO);
176
+ }
177
+ }];
178
+ }];
179
+ }
180
+
181
+ - (void)unlockWithPassword:(NSString *)deviceHandle
182
+ password:(NSString *)password
183
+ resolve:(RCTPromiseResolveBlock)resolve
184
+ reject:(RCTPromiseRejectBlock)reject {
185
+ id<YKFConnectionProtocol> connection = [self connectionForHandle:deviceHandle reject:reject];
186
+ if (!connection) return;
187
+
188
+ [connection oathSession:^(YKFOATHSession *_Nullable session, NSError *_Nullable error) {
189
+ if (error || session == nil) {
190
+ reject(@"OATH_ERROR", error ? error.localizedDescription : @"OATH session not available", error);
191
+ return;
192
+ }
193
+ [session unlockWithPassword:password completion:^(NSError *_Nullable error) {
194
+ if (error) {
195
+ reject(@"OATH_ERROR", error.localizedDescription, error);
196
+ return;
197
+ }
198
+ resolve(@YES);
199
+ }];
200
+ }];
201
+ }
202
+
203
+ - (void)unlockWithAccessKey:(NSString *)deviceHandle
204
+ accessKey:(NSString *)accessKey
205
+ resolve:(RCTPromiseResolveBlock)resolve
206
+ reject:(RCTPromiseRejectBlock)reject {
207
+ id<YKFConnectionProtocol> connection = [self connectionForHandle:deviceHandle reject:reject];
208
+ if (!connection) return;
209
+
210
+ [connection oathSession:^(YKFOATHSession *_Nullable session, NSError *_Nullable error) {
211
+ if (error || session == nil) {
212
+ reject(@"OATH_ERROR", error ? error.localizedDescription : @"OATH session not available", error);
213
+ return;
214
+ }
215
+ NSData *keyData = [[NSData alloc] initWithBase64EncodedString:accessKey options:0];
216
+ if (keyData == nil) {
217
+ reject(@"OATH_ERROR", @"Invalid base64 access key", nil);
218
+ return;
219
+ }
220
+ [session unlockWithAccessKey:keyData completion:^(NSError *_Nullable error) {
221
+ if (error) {
222
+ reject(@"OATH_ERROR", error.localizedDescription, error);
223
+ return;
224
+ }
225
+ resolve(@YES);
226
+ }];
227
+ }];
228
+ }
229
+
230
+ - (void)setPassword:(NSString *)deviceHandle
231
+ password:(NSString *)password
232
+ resolve:(RCTPromiseResolveBlock)resolve
233
+ reject:(RCTPromiseRejectBlock)reject {
234
+ id<YKFConnectionProtocol> connection = [self connectionForHandle:deviceHandle reject:reject];
235
+ if (!connection) return;
236
+
237
+ [connection oathSession:^(YKFOATHSession *_Nullable session, NSError *_Nullable error) {
238
+ if (error || session == nil) {
239
+ reject(@"OATH_ERROR", error ? error.localizedDescription : @"OATH session not available", error);
240
+ return;
241
+ }
242
+ [session setPassword:password completion:^(NSError *_Nullable error) {
243
+ if (error) {
244
+ reject(@"OATH_ERROR", error.localizedDescription, error);
245
+ return;
246
+ }
247
+ resolve(nil);
248
+ }];
249
+ }];
250
+ }
251
+
252
+ - (void)setAccessKey:(NSString *)deviceHandle
253
+ accessKey:(NSString *)accessKey
254
+ resolve:(RCTPromiseResolveBlock)resolve
255
+ reject:(RCTPromiseRejectBlock)reject {
256
+ id<YKFConnectionProtocol> connection = [self connectionForHandle:deviceHandle reject:reject];
257
+ if (!connection) return;
258
+
259
+ [connection oathSession:^(YKFOATHSession *_Nullable session, NSError *_Nullable error) {
260
+ if (error || session == nil) {
261
+ reject(@"OATH_ERROR", error ? error.localizedDescription : @"OATH session not available", error);
262
+ return;
263
+ }
264
+ NSData *keyData = [[NSData alloc] initWithBase64EncodedString:accessKey options:0];
265
+ if (keyData == nil) {
266
+ reject(@"OATH_ERROR", @"Invalid base64 access key", nil);
267
+ return;
268
+ }
269
+ [session setAccessKey:keyData completion:^(NSError *_Nullable error) {
270
+ if (error) {
271
+ reject(@"OATH_ERROR", error.localizedDescription, error);
272
+ return;
273
+ }
274
+ resolve(nil);
275
+ }];
276
+ }];
277
+ }
278
+
279
+ - (void)deleteAccessKey:(NSString *)deviceHandle
280
+ resolve:(RCTPromiseResolveBlock)resolve
281
+ reject:(RCTPromiseRejectBlock)reject {
282
+ id<YKFConnectionProtocol> connection = [self connectionForHandle:deviceHandle reject:reject];
283
+ if (!connection) return;
284
+
285
+ [connection oathSession:^(YKFOATHSession *_Nullable session, NSError *_Nullable error) {
286
+ if (error || session == nil) {
287
+ reject(@"OATH_ERROR", error ? error.localizedDescription : @"OATH session not available", error);
288
+ return;
289
+ }
290
+ [session deleteAccessKeyWithCompletion:^(NSError *_Nullable error) {
291
+ if (error) {
292
+ reject(@"OATH_ERROR", error.localizedDescription, error);
293
+ return;
294
+ }
295
+ resolve(nil);
296
+ }];
297
+ }];
298
+ }
299
+
300
+ - (void)getCredentials:(NSString *)deviceHandle
301
+ resolve:(RCTPromiseResolveBlock)resolve
302
+ reject:(RCTPromiseRejectBlock)reject {
303
+ id<YKFConnectionProtocol> connection = [self connectionForHandle:deviceHandle reject:reject];
304
+ if (!connection) return;
305
+
306
+ [connection oathSession:^(YKFOATHSession *_Nullable session, NSError *_Nullable error) {
307
+ if (error || session == nil) {
308
+ reject(@"OATH_ERROR", error ? error.localizedDescription : @"OATH session not available", error);
309
+ return;
310
+ }
311
+ [session listCredentialsWithCompletion:^(NSArray<YKFOATHCredential *> *_Nullable credentials, NSError *_Nullable error) {
312
+ if (error) {
313
+ reject(@"OATH_ERROR", error.localizedDescription, error);
314
+ return;
315
+ }
316
+ NSMutableArray *result = [NSMutableArray arrayWithCapacity:credentials.count];
317
+ for (YKFOATHCredential *credential in credentials) {
318
+ [result addObject:[self dictionaryFromCredential:credential]];
319
+ }
320
+ resolve(@{@"credentials": result});
321
+ }];
322
+ }];
323
+ }
324
+
325
+ - (void)calculateCodes:(NSString *)deviceHandle
326
+ timestamp:(NSNumber *)timestamp
327
+ resolve:(RCTPromiseResolveBlock)resolve
328
+ reject:(RCTPromiseRejectBlock)reject {
329
+ id<YKFConnectionProtocol> connection = [self connectionForHandle:deviceHandle reject:reject];
330
+ if (!connection) return;
331
+
332
+ [connection oathSession:^(YKFOATHSession *_Nullable session, NSError *_Nullable error) {
333
+ if (error || session == nil) {
334
+ reject(@"OATH_ERROR", error ? error.localizedDescription : @"OATH session not available", error);
335
+ return;
336
+ }
337
+
338
+ YKFOATHSessionCalculateAllCompletionBlock completion = ^(NSArray<YKFOATHCredentialWithCode *> *_Nullable credentialsWithCodes, NSError *_Nullable error) {
339
+ if (error) {
340
+ reject(@"OATH_ERROR", error.localizedDescription, error);
341
+ return;
342
+ }
343
+ NSMutableArray *result = [NSMutableArray arrayWithCapacity:credentialsWithCodes.count];
344
+ for (YKFOATHCredentialWithCode *entry in credentialsWithCodes) {
345
+ NSMutableDictionary *dict = [NSMutableDictionary dictionary];
346
+ dict[@"credential"] = [self dictionaryFromCredential:entry.credential];
347
+ if (entry.code) {
348
+ dict[@"code"] = [self dictionaryFromCode:entry.code];
349
+ }
350
+ [result addObject:dict];
351
+ }
352
+ resolve(@{@"codes": result});
353
+ };
354
+
355
+ if (timestamp) {
356
+ [session calculateAllWithTimestamp:[NSDate dateWithTimeIntervalSince1970:timestamp.doubleValue / 1000.0] completion:completion];
357
+ } else {
358
+ [session calculateAllWithCompletion:completion];
359
+ }
360
+ }];
361
+ }
362
+
363
+ - (void)calculateResponse:(NSString *)deviceHandle
364
+ credentialId:(NSString *)credentialId
365
+ challenge:(NSString *)challenge
366
+ resolve:(RCTPromiseResolveBlock)resolve
367
+ reject:(RCTPromiseRejectBlock)reject {
368
+ id<YKFConnectionProtocol> connection = [self connectionForHandle:deviceHandle reject:reject];
369
+ if (!connection) return;
370
+
371
+ [connection oathSession:^(YKFOATHSession *_Nullable session, NSError *_Nullable error) {
372
+ if (error || session == nil) {
373
+ reject(@"OATH_ERROR", error ? error.localizedDescription : @"OATH session not available", error);
374
+ return;
375
+ }
376
+
377
+ NSData *credentialIdData = [[NSData alloc] initWithBase64EncodedString:credentialId options:0];
378
+ NSData *challengeData = [[NSData alloc] initWithBase64EncodedString:challenge options:0];
379
+ if (challengeData == nil) {
380
+ reject(@"OATH_ERROR", @"Invalid base64 challenge", nil);
381
+ return;
382
+ }
383
+
384
+ [session calculateResponseForCredentialID:credentialIdData ?: [NSData data]
385
+ challenge:challengeData
386
+ completion:^(NSData *_Nullable response, NSError *_Nullable error) {
387
+ if (error) {
388
+ reject(@"OATH_ERROR", error.localizedDescription, error);
389
+ return;
390
+ }
391
+ resolve([response base64EncodedStringWithOptions:0]);
392
+ }];
393
+ }];
394
+ }
395
+
396
+ - (void)calculateCode:(NSString *)deviceHandle
397
+ credentialId:(NSString *)credentialId
398
+ timestamp:(NSNumber *)timestamp
399
+ resolve:(RCTPromiseResolveBlock)resolve
400
+ reject:(RCTPromiseRejectBlock)reject {
401
+ id<YKFConnectionProtocol> connection = [self connectionForHandle:deviceHandle reject:reject];
402
+ if (!connection) return;
403
+
404
+ [connection oathSession:^(YKFOATHSession *_Nullable session, NSError *_Nullable error) {
405
+ if (error || session == nil) {
406
+ reject(@"OATH_ERROR", error ? error.localizedDescription : @"OATH session not available", error);
407
+ return;
408
+ }
409
+
410
+ [session listCredentialsWithCompletion:^(NSArray<YKFOATHCredential *> *_Nullable credentials, NSError *_Nullable error) {
411
+ if (error) {
412
+ reject(@"OATH_ERROR", error.localizedDescription, error);
413
+ return;
414
+ }
415
+ YKFOATHCredential *credential = [self findCredentialWithId:credentialId inCredentialList:credentials];
416
+ if (credential == nil) {
417
+ reject(@"OATH_ERROR", @"Credential not found", nil);
418
+ return;
419
+ }
420
+
421
+ YKFOATHSessionCalculateCompletionBlock completion = ^(YKFOATHCode *_Nullable code, NSError *_Nullable error) {
422
+ if (error) {
423
+ reject(@"OATH_ERROR", error.localizedDescription, error);
424
+ return;
425
+ }
426
+ resolve([self dictionaryFromCode:code]);
427
+ };
428
+
429
+ if (timestamp) {
430
+ [session calculateCredential:credential timestamp:[NSDate dateWithTimeIntervalSince1970:timestamp.doubleValue / 1000.0] completion:completion];
431
+ } else {
432
+ [session calculateCredential:credential completion:completion];
433
+ }
434
+ }];
435
+ }];
436
+ }
437
+
438
+ - (void)putCredential:(NSString *)deviceHandle
439
+ credentialData:(JS::NativeYubikitOath::CredentialData &)credentialData
440
+ requireTouch:(BOOL)requireTouch
441
+ resolve:(RCTPromiseResolveBlock)resolve
442
+ reject:(RCTPromiseRejectBlock)reject {
443
+ id<YKFConnectionProtocol> connection = [self connectionForHandle:deviceHandle reject:reject];
444
+ if (!connection) return;
445
+
446
+ NSData *secret = [[NSData alloc] initWithBase64EncodedString:credentialData.secret() options:0];
447
+ if (secret == nil) {
448
+ reject(@"OATH_ERROR", @"Invalid base64 secret", nil);
449
+ return;
450
+ }
451
+
452
+ YKFOATHCredentialTemplate *credentialTemplate = [[YKFOATHCredentialTemplate alloc]
453
+ initWithType:[self oathTypeFromString:credentialData.oathType()]
454
+ algorithm:[self algorithmFromString:credentialData.hashAlgorithm()]
455
+ secret:secret
456
+ issuer:credentialData.issuer()
457
+ accountName:credentialData.accountName()
458
+ digits:(NSUInteger)credentialData.digits()
459
+ period:(NSUInteger)credentialData.period()
460
+ counter:(UInt32)credentialData.counter()];
461
+
462
+ if (credentialTemplate == nil) {
463
+ reject(@"OATH_ERROR", @"Invalid credential data", nil);
464
+ return;
465
+ }
466
+
467
+ [connection oathSession:^(YKFOATHSession *_Nullable session, NSError *_Nullable error) {
468
+ if (error || session == nil) {
469
+ reject(@"OATH_ERROR", error ? error.localizedDescription : @"OATH session not available", error);
470
+ return;
471
+ }
472
+ [session putCredentialTemplate:credentialTemplate requiresTouch:requireTouch completion:^(NSError *_Nullable error) {
473
+ if (error) {
474
+ reject(@"OATH_ERROR", error.localizedDescription, error);
475
+ return;
476
+ }
477
+
478
+ // Build a credential-like response from the template data.
479
+ NSDictionary *idDict = @{
480
+ @"oathType": credentialData.oathType(),
481
+ @"accountName": credentialData.accountName(),
482
+ @"issuer": credentialData.issuer() ?: @"",
483
+ @"period": @(credentialData.period())
484
+ };
485
+ NSData *idData = [NSJSONSerialization dataWithJSONObject:idDict options:0 error:nil];
486
+ NSString *credentialId = [idData base64EncodedStringWithOptions:0];
487
+
488
+ NSMutableDictionary *result = [NSMutableDictionary dictionary];
489
+ result[@"id"] = credentialId ?: @"";
490
+ result[@"oathType"] = credentialData.oathType();
491
+ result[@"accountName"] = credentialData.accountName();
492
+ result[@"issuer"] = credentialData.issuer() ?: [NSNull null];
493
+ result[@"period"] = @(credentialData.period());
494
+ result[@"touchRequired"] = @(requireTouch);
495
+ resolve(result);
496
+ }];
497
+ }];
498
+ }
499
+
500
+ - (void)deleteCredential:(NSString *)deviceHandle
501
+ credentialId:(NSString *)credentialId
502
+ resolve:(RCTPromiseResolveBlock)resolve
503
+ reject:(RCTPromiseRejectBlock)reject {
504
+ id<YKFConnectionProtocol> connection = [self connectionForHandle:deviceHandle reject:reject];
505
+ if (!connection) return;
506
+
507
+ [connection oathSession:^(YKFOATHSession *_Nullable session, NSError *_Nullable error) {
508
+ if (error || session == nil) {
509
+ reject(@"OATH_ERROR", error ? error.localizedDescription : @"OATH session not available", error);
510
+ return;
511
+ }
512
+
513
+ [session listCredentialsWithCompletion:^(NSArray<YKFOATHCredential *> *_Nullable credentials, NSError *_Nullable error) {
514
+ if (error) {
515
+ reject(@"OATH_ERROR", error.localizedDescription, error);
516
+ return;
517
+ }
518
+ YKFOATHCredential *credential = [self findCredentialWithId:credentialId inCredentialList:credentials];
519
+ if (credential == nil) {
520
+ reject(@"OATH_ERROR", @"Credential not found", nil);
521
+ return;
522
+ }
523
+
524
+ [session deleteCredential:credential completion:^(NSError *_Nullable error) {
525
+ if (error) {
526
+ reject(@"OATH_ERROR", error.localizedDescription, error);
527
+ return;
528
+ }
529
+ resolve(nil);
530
+ }];
531
+ }];
532
+ }];
533
+ }
534
+
535
+ - (void)renameCredential:(NSString *)deviceHandle
536
+ credentialId:(NSString *)credentialId
537
+ newAccountName:(NSString *)newAccountName
538
+ newIssuer:(NSString *)newIssuer
539
+ resolve:(RCTPromiseResolveBlock)resolve
540
+ reject:(RCTPromiseRejectBlock)reject {
541
+ id<YKFConnectionProtocol> connection = [self connectionForHandle:deviceHandle reject:reject];
542
+ if (!connection) return;
543
+
544
+ [connection oathSession:^(YKFOATHSession *_Nullable session, NSError *_Nullable error) {
545
+ if (error || session == nil) {
546
+ reject(@"OATH_ERROR", error ? error.localizedDescription : @"OATH session not available", error);
547
+ return;
548
+ }
549
+
550
+ [session listCredentialsWithCompletion:^(NSArray<YKFOATHCredential *> *_Nullable credentials, NSError *_Nullable error) {
551
+ if (error) {
552
+ reject(@"OATH_ERROR", error.localizedDescription, error);
553
+ return;
554
+ }
555
+ YKFOATHCredential *credential = [self findCredentialWithId:credentialId inCredentialList:credentials];
556
+ if (credential == nil) {
557
+ reject(@"OATH_ERROR", @"Credential not found", nil);
558
+ return;
559
+ }
560
+
561
+ [session renameCredential:credential
562
+ newIssuer:newIssuer
563
+ newAccount:newAccountName
564
+ completion:^(NSError *_Nullable error) {
565
+ if (error) {
566
+ reject(@"OATH_ERROR", error.localizedDescription, error);
567
+ return;
568
+ }
569
+
570
+ NSMutableDictionary *result = [NSMutableDictionary dictionaryWithDictionary:[self dictionaryFromCredential:credential]];
571
+ result[@"accountName"] = newAccountName;
572
+ result[@"issuer"] = newIssuer ?: [NSNull null];
573
+ resolve(result);
574
+ }];
575
+ }];
576
+ }];
577
+ }
578
+
579
+ - (std::shared_ptr<facebook::react::TurboModule>)getTurboModule:(const facebook::react::ObjCTurboModule::InitParams &)params {
580
+ return std::make_shared<facebook::react::NativeYubikitOathSpecJSI>(params);
581
+ }
582
+
583
+ @end
@@ -0,0 +1,5 @@
1
+ #import <YubikitSpec/YubikitSpec.h>
2
+
3
+ @interface YubikitOpenPgp : NSObject <NativeYubikitOpenPgpSpec>
4
+
5
+ @end