@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.
- package/LICENSE +20 -0
- package/README.md +126 -0
- package/RNYubikit.podspec +22 -0
- package/android/build.gradle +63 -0
- package/android/src/main/AndroidManifest.xml +9 -0
- package/android/src/main/java/com/yubikit/YubiKitManagerHolder.kt +330 -0
- package/android/src/main/java/com/yubikit/YubikitCoreModule.kt +142 -0
- package/android/src/main/java/com/yubikit/YubikitFidoModule.kt +418 -0
- package/android/src/main/java/com/yubikit/YubikitManagementModule.kt +132 -0
- package/android/src/main/java/com/yubikit/YubikitOathModule.kt +224 -0
- package/android/src/main/java/com/yubikit/YubikitOpenPgpModule.kt +317 -0
- package/android/src/main/java/com/yubikit/YubikitPackage.kt +44 -0
- package/android/src/main/java/com/yubikit/YubikitPivModule.kt +310 -0
- package/android/src/main/java/com/yubikit/YubikitSupportModule.kt +108 -0
- package/android/src/main/java/com/yubikit/YubikitYubiOtpModule.kt +248 -0
- package/android/src/main/java/com/yubikit/utils/YubikitUtils.kt +321 -0
- package/ios/YubikitCore.h +5 -0
- package/ios/YubikitCore.mm +101 -0
- package/ios/YubikitDeviceInfoHelper.h +13 -0
- package/ios/YubikitDeviceInfoHelper.mm +110 -0
- package/ios/YubikitFido.h +5 -0
- package/ios/YubikitFido.mm +373 -0
- package/ios/YubikitManagement.h +5 -0
- package/ios/YubikitManagement.mm +153 -0
- package/ios/YubikitManager.h +27 -0
- package/ios/YubikitManager.m +169 -0
- package/ios/YubikitOath.h +5 -0
- package/ios/YubikitOath.mm +583 -0
- package/ios/YubikitOpenPgp.h +5 -0
- package/ios/YubikitOpenPgp.mm +210 -0
- package/ios/YubikitPiv.h +5 -0
- package/ios/YubikitPiv.mm +813 -0
- package/ios/YubikitSupport.h +5 -0
- package/ios/YubikitSupport.mm +58 -0
- package/ios/YubikitYubiOtp.h +5 -0
- package/ios/YubikitYubiOtp.mm +120 -0
- package/lib/module/core.js +34 -0
- package/lib/module/core.js.map +1 -0
- package/lib/module/fido.js +40 -0
- package/lib/module/fido.js.map +1 -0
- package/lib/module/hooks/index.js +5 -0
- package/lib/module/hooks/index.js.map +1 -0
- package/lib/module/hooks/use-yubikey.js +12 -0
- package/lib/module/hooks/use-yubikey.js.map +1 -0
- package/lib/module/hooks/yubikey-provider.js +85 -0
- package/lib/module/hooks/yubikey-provider.js.map +1 -0
- package/lib/module/index.js +12 -0
- package/lib/module/index.js.map +1 -0
- package/lib/module/management.js +17 -0
- package/lib/module/management.js.map +1 -0
- package/lib/module/oath.js +53 -0
- package/lib/module/oath.js.map +1 -0
- package/lib/module/openpgp.js +89 -0
- package/lib/module/openpgp.js.map +1 -0
- package/lib/module/package.json +1 -0
- package/lib/module/piv.js +71 -0
- package/lib/module/piv.js.map +1 -0
- package/lib/module/specs/NativeYubikitCore.js +10 -0
- package/lib/module/specs/NativeYubikitCore.js.map +1 -0
- package/lib/module/specs/NativeYubikitFido.js +5 -0
- package/lib/module/specs/NativeYubikitFido.js.map +1 -0
- package/lib/module/specs/NativeYubikitManagement.js +5 -0
- package/lib/module/specs/NativeYubikitManagement.js.map +1 -0
- package/lib/module/specs/NativeYubikitOath.js +5 -0
- package/lib/module/specs/NativeYubikitOath.js.map +1 -0
- package/lib/module/specs/NativeYubikitOpenPgp.js +5 -0
- package/lib/module/specs/NativeYubikitOpenPgp.js.map +1 -0
- package/lib/module/specs/NativeYubikitPiv.js +5 -0
- package/lib/module/specs/NativeYubikitPiv.js.map +1 -0
- package/lib/module/specs/NativeYubikitSupport.js +5 -0
- package/lib/module/specs/NativeYubikitSupport.js.map +1 -0
- package/lib/module/specs/NativeYubikitYubiOtp.js +5 -0
- package/lib/module/specs/NativeYubikitYubiOtp.js.map +1 -0
- package/lib/module/support.js +11 -0
- package/lib/module/support.js.map +1 -0
- package/lib/module/types.js +2 -0
- package/lib/module/types.js.map +1 -0
- package/lib/module/yubiotp.js +32 -0
- package/lib/module/yubiotp.js.map +1 -0
- package/lib/typescript/package.json +1 -0
- package/lib/typescript/src/core.d.ts +14 -0
- package/lib/typescript/src/core.d.ts.map +1 -0
- package/lib/typescript/src/fido.d.ts +24 -0
- package/lib/typescript/src/fido.d.ts.map +1 -0
- package/lib/typescript/src/hooks/index.d.ts +3 -0
- package/lib/typescript/src/hooks/index.d.ts.map +1 -0
- package/lib/typescript/src/hooks/use-yubikey.d.ts +3 -0
- package/lib/typescript/src/hooks/use-yubikey.d.ts.map +1 -0
- package/lib/typescript/src/hooks/yubikey-provider.d.ts +23 -0
- package/lib/typescript/src/hooks/yubikey-provider.d.ts.map +1 -0
- package/lib/typescript/src/index.d.ts +10 -0
- package/lib/typescript/src/index.d.ts.map +1 -0
- package/lib/typescript/src/management.d.ts +8 -0
- package/lib/typescript/src/management.d.ts.map +1 -0
- package/lib/typescript/src/oath.d.ts +20 -0
- package/lib/typescript/src/oath.d.ts.map +1 -0
- package/lib/typescript/src/openpgp.d.ts +35 -0
- package/lib/typescript/src/openpgp.d.ts.map +1 -0
- package/lib/typescript/src/piv.d.ts +26 -0
- package/lib/typescript/src/piv.d.ts.map +1 -0
- package/lib/typescript/src/specs/NativeYubikitCore.d.ts +49 -0
- package/lib/typescript/src/specs/NativeYubikitCore.d.ts.map +1 -0
- package/lib/typescript/src/specs/NativeYubikitFido.d.ts +121 -0
- package/lib/typescript/src/specs/NativeYubikitFido.d.ts.map +1 -0
- package/lib/typescript/src/specs/NativeYubikitManagement.d.ts +53 -0
- package/lib/typescript/src/specs/NativeYubikitManagement.d.ts.map +1 -0
- package/lib/typescript/src/specs/NativeYubikitOath.d.ts +70 -0
- package/lib/typescript/src/specs/NativeYubikitOath.d.ts.map +1 -0
- package/lib/typescript/src/specs/NativeYubikitOpenPgp.d.ts +53 -0
- package/lib/typescript/src/specs/NativeYubikitOpenPgp.d.ts.map +1 -0
- package/lib/typescript/src/specs/NativeYubikitPiv.d.ts +47 -0
- package/lib/typescript/src/specs/NativeYubikitPiv.d.ts.map +1 -0
- package/lib/typescript/src/specs/NativeYubikitSupport.d.ts +49 -0
- package/lib/typescript/src/specs/NativeYubikitSupport.d.ts.map +1 -0
- package/lib/typescript/src/specs/NativeYubikitYubiOtp.d.ts +45 -0
- package/lib/typescript/src/specs/NativeYubikitYubiOtp.d.ts.map +1 -0
- package/lib/typescript/src/support.d.ts +6 -0
- package/lib/typescript/src/support.d.ts.map +1 -0
- package/lib/typescript/src/types.d.ts +293 -0
- package/lib/typescript/src/types.d.ts.map +1 -0
- package/lib/typescript/src/yubiotp.d.ts +13 -0
- package/lib/typescript/src/yubiotp.d.ts.map +1 -0
- package/package.json +181 -0
- package/src/core.ts +58 -0
- package/src/fido.ts +112 -0
- package/src/hooks/index.tsx +2 -0
- package/src/hooks/use-yubikey.tsx +10 -0
- package/src/hooks/yubikey-provider.tsx +146 -0
- package/src/index.tsx +10 -0
- package/src/management.ts +44 -0
- package/src/oath.ts +134 -0
- package/src/openpgp.ts +220 -0
- package/src/piv.ts +193 -0
- package/src/specs/NativeYubikitCore.ts +68 -0
- package/src/specs/NativeYubikitFido.ts +149 -0
- package/src/specs/NativeYubikitManagement.ts +78 -0
- package/src/specs/NativeYubikitOath.ts +110 -0
- package/src/specs/NativeYubikitOpenPgp.ts +148 -0
- package/src/specs/NativeYubikitPiv.ts +108 -0
- package/src/specs/NativeYubikitSupport.ts +61 -0
- package/src/specs/NativeYubikitYubiOtp.ts +82 -0
- package/src/support.ts +18 -0
- package/src/types.ts +404 -0
- package/src/yubiotp.ts +96 -0
|
@@ -0,0 +1,813 @@
|
|
|
1
|
+
#import "YubikitPiv.h"
|
|
2
|
+
#import "YubikitManager.h"
|
|
3
|
+
#import <YubiKit/YubiKit.h>
|
|
4
|
+
|
|
5
|
+
@implementation YubikitPiv
|
|
6
|
+
|
|
7
|
+
RCT_EXPORT_MODULE(YubikitPiv)
|
|
8
|
+
|
|
9
|
+
#pragma mark - Helpers
|
|
10
|
+
|
|
11
|
+
- (id<YKFConnectionProtocol>)connectionForHandle:(NSString *)deviceHandle
|
|
12
|
+
reject:(RCTPromiseRejectBlock)reject {
|
|
13
|
+
id<YKFConnectionProtocol> connection = [[YubikitManager shared] connectionForHandle:deviceHandle];
|
|
14
|
+
if (connection == nil) {
|
|
15
|
+
reject(@"CONNECTION_ERROR", @"No device found for handle", nil);
|
|
16
|
+
}
|
|
17
|
+
return connection;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
- (YKFPIVSlot)slotFromString:(NSString *)slot {
|
|
21
|
+
if ([slot isEqualToString:@"SIGNATURE"]) return YKFPIVSlotSignature;
|
|
22
|
+
if ([slot isEqualToString:@"KEY_MANAGEMENT"]) return YKFPIVSlotKeyManagement;
|
|
23
|
+
if ([slot isEqualToString:@"CARD_AUTH"]) return YKFPIVSlotCardAuth;
|
|
24
|
+
if ([slot isEqualToString:@"ATTESTATION"]) return YKFPIVSlotAttestation;
|
|
25
|
+
if ([slot hasPrefix:@"RETIRED"]) {
|
|
26
|
+
NSInteger index = [[slot substringFromIndex:7] integerValue];
|
|
27
|
+
if (index >= 1 && index <= 20) {
|
|
28
|
+
return (YKFPIVSlot)(0x82 + (index - 1));
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
return YKFPIVSlotAuthentication;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
- (YKFPIVKeyType)keyTypeFromString:(NSString *)keyType {
|
|
35
|
+
if ([keyType isEqualToString:@"RSA1024"]) return YKFPIVKeyTypeRSA1024;
|
|
36
|
+
if ([keyType isEqualToString:@"RSA2048"]) return YKFPIVKeyTypeRSA2048;
|
|
37
|
+
if ([keyType isEqualToString:@"RSA3072"]) return YKFPIVKeyTypeRSA3072;
|
|
38
|
+
if ([keyType isEqualToString:@"RSA4096"]) return YKFPIVKeyTypeRSA4096;
|
|
39
|
+
if ([keyType isEqualToString:@"ECCP256"]) return YKFPIVKeyTypeECCP256;
|
|
40
|
+
if ([keyType isEqualToString:@"ECCP384"]) return YKFPIVKeyTypeECCP384;
|
|
41
|
+
return YKFPIVKeyTypeUnknown;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
- (NSString *)stringFromKeyType:(YKFPIVKeyType)keyType {
|
|
45
|
+
switch (keyType) {
|
|
46
|
+
case YKFPIVKeyTypeRSA1024: return @"RSA1024";
|
|
47
|
+
case YKFPIVKeyTypeRSA2048: return @"RSA2048";
|
|
48
|
+
case YKFPIVKeyTypeRSA3072: return @"RSA3072";
|
|
49
|
+
case YKFPIVKeyTypeRSA4096: return @"RSA4096";
|
|
50
|
+
case YKFPIVKeyTypeECCP256: return @"ECCP256";
|
|
51
|
+
case YKFPIVKeyTypeECCP384: return @"ECCP384";
|
|
52
|
+
default: return @"";
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
- (YKFPIVPinPolicy)pinPolicyFromString:(NSString *)policy {
|
|
57
|
+
if ([policy isEqualToString:@"NEVER"]) return YKFPIVPinPolicyNever;
|
|
58
|
+
if ([policy isEqualToString:@"ONCE"]) return YKFPIVPinPolicyOnce;
|
|
59
|
+
if ([policy isEqualToString:@"ALWAYS"]) return YKFPIVPinPolicyAlways;
|
|
60
|
+
if ([policy isEqualToString:@"MATCH_ONCE"]) return YKFPIVPinPolicyMatchOnce;
|
|
61
|
+
if ([policy isEqualToString:@"MATCH_ALWAYS"]) return YKFPIVPinPolicyMatchAlways;
|
|
62
|
+
return YKFPIVPinPolicyDefault;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
- (NSString *)stringFromPinPolicy:(YKFPIVPinPolicy)policy {
|
|
66
|
+
switch (policy) {
|
|
67
|
+
case YKFPIVPinPolicyNever: return @"NEVER";
|
|
68
|
+
case YKFPIVPinPolicyOnce: return @"ONCE";
|
|
69
|
+
case YKFPIVPinPolicyAlways: return @"ALWAYS";
|
|
70
|
+
case YKFPIVPinPolicyMatchOnce: return @"MATCH_ONCE";
|
|
71
|
+
case YKFPIVPinPolicyMatchAlways: return @"MATCH_ALWAYS";
|
|
72
|
+
default: return @"DEFAULT";
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
- (YKFPIVTouchPolicy)touchPolicyFromString:(NSString *)policy {
|
|
77
|
+
if ([policy isEqualToString:@"NEVER"]) return YKFPIVTouchPolicyNever;
|
|
78
|
+
if ([policy isEqualToString:@"ALWAYS"]) return YKFPIVTouchPolicyAlways;
|
|
79
|
+
if ([policy isEqualToString:@"CACHED"]) return YKFPIVTouchPolicyCached;
|
|
80
|
+
return YKFPIVTouchPolicyDefault;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
- (NSString *)stringFromTouchPolicy:(YKFPIVTouchPolicy)policy {
|
|
84
|
+
switch (policy) {
|
|
85
|
+
case YKFPIVTouchPolicyNever: return @"NEVER";
|
|
86
|
+
case YKFPIVTouchPolicyAlways: return @"ALWAYS";
|
|
87
|
+
case YKFPIVTouchPolicyCached: return @"CACHED";
|
|
88
|
+
default: return @"DEFAULT";
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
- (YKFPIVManagementKeyType *)managementKeyTypeFromString:(NSString *)keyType {
|
|
93
|
+
if ([keyType isEqualToString:@"AES128"]) return [YKFPIVManagementKeyType AES128];
|
|
94
|
+
if ([keyType isEqualToString:@"AES192"]) return [YKFPIVManagementKeyType AES192];
|
|
95
|
+
if ([keyType isEqualToString:@"AES256"]) return [YKFPIVManagementKeyType AES256];
|
|
96
|
+
return [YKFPIVManagementKeyType TripleDES];
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
// The "authenticate" method doesn't receive an explicit key type from JS, so the
|
|
100
|
+
// management key type is inferred from its length. 24-byte keys default to TDES
|
|
101
|
+
// since that's the YubiKey factory-default management key type.
|
|
102
|
+
- (YKFPIVManagementKeyType *)managementKeyTypeFromKeyData:(NSData *)keyData {
|
|
103
|
+
switch (keyData.length) {
|
|
104
|
+
case 16: return [YKFPIVManagementKeyType AES128];
|
|
105
|
+
case 32: return [YKFPIVManagementKeyType AES256];
|
|
106
|
+
default: return [YKFPIVManagementKeyType TripleDES];
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
- (NSString *)stringFromManagementKeyType:(YKFPIVManagementKeyType *)keyType {
|
|
111
|
+
if ([keyType.name isEqualToString:YKFPIVManagementKeyTypeAES]) {
|
|
112
|
+
switch (keyType.keyLenght) {
|
|
113
|
+
case 16: return @"AES128";
|
|
114
|
+
case 24: return @"AES192";
|
|
115
|
+
case 32: return @"AES256";
|
|
116
|
+
default: return @"TDES";
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
return @"TDES";
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
- (NSString *)base64FromSecCertificate:(SecCertificateRef)certificate {
|
|
123
|
+
if (certificate == NULL) return nil;
|
|
124
|
+
NSData *data = (__bridge_transfer NSData *)SecCertificateCopyData(certificate);
|
|
125
|
+
return [data base64EncodedStringWithOptions:0];
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
- (NSString *)base64FromSecKey:(SecKeyRef)key {
|
|
129
|
+
if (key == NULL) return nil;
|
|
130
|
+
CFErrorRef error = NULL;
|
|
131
|
+
NSData *data = (__bridge_transfer NSData *)SecKeyCopyExternalRepresentation(key, &error);
|
|
132
|
+
if (error || data == nil) return nil;
|
|
133
|
+
return [data base64EncodedStringWithOptions:0];
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
// The SDK's rawSignOrDecrypt is exposed as two distinct methods (sign / decrypt),
|
|
137
|
+
// while the JS API takes no explicit intent. Decryption is only meaningful for RSA
|
|
138
|
+
// keys in the KEY_MANAGEMENT slot on this SDK (EC keys use ECDH instead), so that
|
|
139
|
+
// combination is treated as decrypt; everything else is treated as sign. (The two
|
|
140
|
+
// paths are mathematically equivalent raw RSA operations when using the "Raw"
|
|
141
|
+
// algorithm variants below, so this choice mainly preserves intent for callers.)
|
|
142
|
+
- (BOOL)isDecryptOperationForSlot:(YKFPIVSlot)pivSlot keyType:(YKFPIVKeyType)pivKeyType {
|
|
143
|
+
BOOL isRsa = pivKeyType == YKFPIVKeyTypeRSA1024 || pivKeyType == YKFPIVKeyTypeRSA2048;
|
|
144
|
+
return isRsa && pivSlot == YKFPIVSlotKeyManagement;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
// "Raw"/plain-digest algorithm variants make YKFPIVPadding treat the payload as an
|
|
148
|
+
// already-formed, pass-through block (no extra hashing or padding added), matching
|
|
149
|
+
// the Android SDK's rawSignOrDecrypt, which sends the payload to the card unmodified
|
|
150
|
+
// (aside from the length normalization in normalizedPayload:forKeyType:error: below).
|
|
151
|
+
// Note: YKFPIVPadding only recognizes the SHA-suffixed "Digest" EC constants (they're
|
|
152
|
+
// all treated identically, as pure pass-through) — the unsuffixed
|
|
153
|
+
// kSecKeyAlgorithmECDSASignatureDigestX962 is NOT one of them and would silently
|
|
154
|
+
// zero-pad the payload instead of forwarding it, so it must not be used here.
|
|
155
|
+
- (SecKeyAlgorithm)signAlgorithmForKeyType:(YKFPIVKeyType)pivKeyType {
|
|
156
|
+
switch (pivKeyType) {
|
|
157
|
+
case YKFPIVKeyTypeECCP256:
|
|
158
|
+
return kSecKeyAlgorithmECDSASignatureDigestX962SHA256;
|
|
159
|
+
case YKFPIVKeyTypeECCP384:
|
|
160
|
+
return kSecKeyAlgorithmECDSASignatureDigestX962SHA384;
|
|
161
|
+
default:
|
|
162
|
+
return kSecKeyAlgorithmRSASignatureRaw;
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
// YKFPIVKeyType.h declares this as a plain C function with no `extern "C"` guard,
|
|
167
|
+
// so calling it from Objective-C++ (.mm) applies C++ name mangling that doesn't
|
|
168
|
+
// match the C-linkage symbol the SDK actually exports, causing an undefined-symbol
|
|
169
|
+
// link error. Reimplement it locally instead (values match YKFPIVSizeFromKeyType).
|
|
170
|
+
- (NSInteger)byteLengthForKeyType:(YKFPIVKeyType)pivKeyType {
|
|
171
|
+
switch (pivKeyType) {
|
|
172
|
+
case YKFPIVKeyTypeECCP256: return 256 / 8;
|
|
173
|
+
case YKFPIVKeyTypeECCP384: return 384 / 8;
|
|
174
|
+
case YKFPIVKeyTypeRSA1024: return 1024 / 8;
|
|
175
|
+
case YKFPIVKeyTypeRSA2048: return 2048 / 8;
|
|
176
|
+
case YKFPIVKeyTypeRSA3072: return 3072 / 8;
|
|
177
|
+
case YKFPIVKeyTypeRSA4096: return 4096 / 8;
|
|
178
|
+
default: return 0;
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
// Mirrors PivSession.rawSignOrDecrypt on Android: left-pad short payloads with
|
|
183
|
+
// leading zero bytes, truncate long EC payloads, and reject long RSA payloads,
|
|
184
|
+
// before handing off to the card's raw private-key operation.
|
|
185
|
+
- (nullable NSData *)normalizedPayload:(NSData *)payload
|
|
186
|
+
forKeyType:(YKFPIVKeyType)pivKeyType
|
|
187
|
+
error:(NSError **)error {
|
|
188
|
+
NSInteger byteLength = [self byteLengthForKeyType:pivKeyType];
|
|
189
|
+
if (byteLength <= 0 || payload.length == (NSUInteger)byteLength) {
|
|
190
|
+
return payload;
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
BOOL isEC = pivKeyType == YKFPIVKeyTypeECCP256 || pivKeyType == YKFPIVKeyTypeECCP384;
|
|
194
|
+
|
|
195
|
+
if (payload.length > (NSUInteger)byteLength) {
|
|
196
|
+
if (isEC) {
|
|
197
|
+
return [payload subdataWithRange:NSMakeRange(0, (NSUInteger)byteLength)];
|
|
198
|
+
}
|
|
199
|
+
if (error) {
|
|
200
|
+
*error = [NSError errorWithDomain:@"YubikitPiv" code:0
|
|
201
|
+
userInfo:@{NSLocalizedDescriptionKey: @"Payload too large for key"}];
|
|
202
|
+
}
|
|
203
|
+
return nil;
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
NSMutableData *padded = [NSMutableData dataWithLength:(NSUInteger)byteLength];
|
|
207
|
+
[payload getBytes:(uint8_t *)padded.mutableBytes + (byteLength - payload.length) length:payload.length];
|
|
208
|
+
return padded;
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
#pragma mark - Methods
|
|
212
|
+
|
|
213
|
+
- (void)reset:(NSString *)deviceHandle
|
|
214
|
+
resolve:(RCTPromiseResolveBlock)resolve
|
|
215
|
+
reject:(RCTPromiseRejectBlock)reject {
|
|
216
|
+
id<YKFConnectionProtocol> connection = [self connectionForHandle:deviceHandle reject:reject];
|
|
217
|
+
if (!connection) return;
|
|
218
|
+
|
|
219
|
+
[connection pivSession:^(YKFPIVSession *_Nullable session, NSError *_Nullable error) {
|
|
220
|
+
if (error || session == nil) {
|
|
221
|
+
reject(@"PIV_ERROR", error ? error.localizedDescription : @"PIV session not available", error);
|
|
222
|
+
return;
|
|
223
|
+
}
|
|
224
|
+
[session resetWithCompletion:^(NSError *_Nullable error) {
|
|
225
|
+
if (error) {
|
|
226
|
+
reject(@"PIV_ERROR", error.localizedDescription, error);
|
|
227
|
+
return;
|
|
228
|
+
}
|
|
229
|
+
resolve(nil);
|
|
230
|
+
}];
|
|
231
|
+
}];
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
- (void)getSerialNumber:(NSString *)deviceHandle
|
|
235
|
+
resolve:(RCTPromiseResolveBlock)resolve
|
|
236
|
+
reject:(RCTPromiseRejectBlock)reject {
|
|
237
|
+
id<YKFConnectionProtocol> connection = [self connectionForHandle:deviceHandle reject:reject];
|
|
238
|
+
if (!connection) return;
|
|
239
|
+
|
|
240
|
+
[connection pivSession:^(YKFPIVSession *_Nullable session, NSError *_Nullable error) {
|
|
241
|
+
if (error || session == nil) {
|
|
242
|
+
reject(@"PIV_ERROR", error ? error.localizedDescription : @"PIV session not available", error);
|
|
243
|
+
return;
|
|
244
|
+
}
|
|
245
|
+
[session getSerialNumberWithCompletion:^(int serialNumber, NSError *_Nullable error) {
|
|
246
|
+
if (error) {
|
|
247
|
+
reject(@"PIV_ERROR", error.localizedDescription, error);
|
|
248
|
+
return;
|
|
249
|
+
}
|
|
250
|
+
resolve(@(serialNumber));
|
|
251
|
+
}];
|
|
252
|
+
}];
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
- (void)authenticate:(NSString *)deviceHandle
|
|
256
|
+
managementKey:(NSString *)managementKey
|
|
257
|
+
resolve:(RCTPromiseResolveBlock)resolve
|
|
258
|
+
reject:(RCTPromiseRejectBlock)reject {
|
|
259
|
+
id<YKFConnectionProtocol> connection = [self connectionForHandle:deviceHandle reject:reject];
|
|
260
|
+
if (!connection) return;
|
|
261
|
+
|
|
262
|
+
NSData *keyData = [[NSData alloc] initWithBase64EncodedString:managementKey options:0];
|
|
263
|
+
if (keyData == nil) {
|
|
264
|
+
reject(@"PIV_ERROR", @"Invalid base64 management key", nil);
|
|
265
|
+
return;
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
[connection pivSession:^(YKFPIVSession *_Nullable session, NSError *_Nullable error) {
|
|
269
|
+
if (error || session == nil) {
|
|
270
|
+
reject(@"PIV_ERROR", error ? error.localizedDescription : @"PIV session not available", error);
|
|
271
|
+
return;
|
|
272
|
+
}
|
|
273
|
+
[session authenticateWithManagementKey:keyData
|
|
274
|
+
type:[self managementKeyTypeFromKeyData:keyData]
|
|
275
|
+
completion:^(NSError *_Nullable error) {
|
|
276
|
+
if (error) {
|
|
277
|
+
reject(@"PIV_ERROR", error.localizedDescription, error);
|
|
278
|
+
return;
|
|
279
|
+
}
|
|
280
|
+
resolve(nil);
|
|
281
|
+
}];
|
|
282
|
+
}];
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
- (void)setManagementKey:(NSString *)deviceHandle
|
|
286
|
+
keyType:(NSString *)keyType
|
|
287
|
+
managementKey:(NSString *)managementKey
|
|
288
|
+
requireTouch:(BOOL)requireTouch
|
|
289
|
+
resolve:(RCTPromiseResolveBlock)resolve
|
|
290
|
+
reject:(RCTPromiseRejectBlock)reject {
|
|
291
|
+
id<YKFConnectionProtocol> connection = [self connectionForHandle:deviceHandle reject:reject];
|
|
292
|
+
if (!connection) return;
|
|
293
|
+
|
|
294
|
+
NSData *keyData = [[NSData alloc] initWithBase64EncodedString:managementKey options:0];
|
|
295
|
+
if (keyData == nil) {
|
|
296
|
+
reject(@"PIV_ERROR", @"Invalid base64 management key", nil);
|
|
297
|
+
return;
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
[connection pivSession:^(YKFPIVSession *_Nullable session, NSError *_Nullable error) {
|
|
301
|
+
if (error || session == nil) {
|
|
302
|
+
reject(@"PIV_ERROR", error ? error.localizedDescription : @"PIV session not available", error);
|
|
303
|
+
return;
|
|
304
|
+
}
|
|
305
|
+
[session setManagementKey:keyData
|
|
306
|
+
type:[self managementKeyTypeFromString:keyType]
|
|
307
|
+
requiresTouch:requireTouch
|
|
308
|
+
completion:^(NSError *_Nullable error) {
|
|
309
|
+
if (error) {
|
|
310
|
+
reject(@"PIV_ERROR", error.localizedDescription, error);
|
|
311
|
+
return;
|
|
312
|
+
}
|
|
313
|
+
resolve(nil);
|
|
314
|
+
}];
|
|
315
|
+
}];
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
- (void)verifyPin:(NSString *)deviceHandle
|
|
319
|
+
pin:(NSString *)pin
|
|
320
|
+
resolve:(RCTPromiseResolveBlock)resolve
|
|
321
|
+
reject:(RCTPromiseRejectBlock)reject {
|
|
322
|
+
id<YKFConnectionProtocol> connection = [self connectionForHandle:deviceHandle reject:reject];
|
|
323
|
+
if (!connection) return;
|
|
324
|
+
|
|
325
|
+
[connection pivSession:^(YKFPIVSession *_Nullable session, NSError *_Nullable error) {
|
|
326
|
+
if (error || session == nil) {
|
|
327
|
+
reject(@"PIV_ERROR", error ? error.localizedDescription : @"PIV session not available", error);
|
|
328
|
+
return;
|
|
329
|
+
}
|
|
330
|
+
[session verifyPin:pin completion:^(int retries, NSError *_Nullable error) {
|
|
331
|
+
if (error) {
|
|
332
|
+
NSString *message = retries > 0
|
|
333
|
+
? [NSString stringWithFormat:@"%@ (%d attempt(s) remaining)", error.localizedDescription, retries]
|
|
334
|
+
: error.localizedDescription;
|
|
335
|
+
reject(@"PIV_ERROR", message, error);
|
|
336
|
+
return;
|
|
337
|
+
}
|
|
338
|
+
resolve(nil);
|
|
339
|
+
}];
|
|
340
|
+
}];
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
- (void)getPinAttempts:(NSString *)deviceHandle
|
|
344
|
+
resolve:(RCTPromiseResolveBlock)resolve
|
|
345
|
+
reject:(RCTPromiseRejectBlock)reject {
|
|
346
|
+
id<YKFConnectionProtocol> connection = [self connectionForHandle:deviceHandle reject:reject];
|
|
347
|
+
if (!connection) return;
|
|
348
|
+
|
|
349
|
+
[connection pivSession:^(YKFPIVSession *_Nullable session, NSError *_Nullable error) {
|
|
350
|
+
if (error || session == nil) {
|
|
351
|
+
reject(@"PIV_ERROR", error ? error.localizedDescription : @"PIV session not available", error);
|
|
352
|
+
return;
|
|
353
|
+
}
|
|
354
|
+
[session getPinAttemptsWithCompletion:^(int retriesRemaining, NSError *_Nullable error) {
|
|
355
|
+
if (error) {
|
|
356
|
+
reject(@"PIV_ERROR", error.localizedDescription, error);
|
|
357
|
+
return;
|
|
358
|
+
}
|
|
359
|
+
resolve(@(retriesRemaining));
|
|
360
|
+
}];
|
|
361
|
+
}];
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
- (void)changePin:(NSString *)deviceHandle
|
|
365
|
+
oldPin:(NSString *)oldPin
|
|
366
|
+
newPin:(NSString *)newPin
|
|
367
|
+
resolve:(RCTPromiseResolveBlock)resolve
|
|
368
|
+
reject:(RCTPromiseRejectBlock)reject {
|
|
369
|
+
id<YKFConnectionProtocol> connection = [self connectionForHandle:deviceHandle reject:reject];
|
|
370
|
+
if (!connection) return;
|
|
371
|
+
|
|
372
|
+
[connection pivSession:^(YKFPIVSession *_Nullable session, NSError *_Nullable error) {
|
|
373
|
+
if (error || session == nil) {
|
|
374
|
+
reject(@"PIV_ERROR", error ? error.localizedDescription : @"PIV session not available", error);
|
|
375
|
+
return;
|
|
376
|
+
}
|
|
377
|
+
[session setPin:newPin oldPin:oldPin completion:^(NSError *_Nullable error) {
|
|
378
|
+
if (error) {
|
|
379
|
+
reject(@"PIV_ERROR", error.localizedDescription, error);
|
|
380
|
+
return;
|
|
381
|
+
}
|
|
382
|
+
resolve(nil);
|
|
383
|
+
}];
|
|
384
|
+
}];
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
- (void)changePuk:(NSString *)deviceHandle
|
|
388
|
+
oldPuk:(NSString *)oldPuk
|
|
389
|
+
newPuk:(NSString *)newPuk
|
|
390
|
+
resolve:(RCTPromiseResolveBlock)resolve
|
|
391
|
+
reject:(RCTPromiseRejectBlock)reject {
|
|
392
|
+
id<YKFConnectionProtocol> connection = [self connectionForHandle:deviceHandle reject:reject];
|
|
393
|
+
if (!connection) return;
|
|
394
|
+
|
|
395
|
+
[connection pivSession:^(YKFPIVSession *_Nullable session, NSError *_Nullable error) {
|
|
396
|
+
if (error || session == nil) {
|
|
397
|
+
reject(@"PIV_ERROR", error ? error.localizedDescription : @"PIV session not available", error);
|
|
398
|
+
return;
|
|
399
|
+
}
|
|
400
|
+
[session setPuk:newPuk oldPuk:oldPuk completion:^(NSError *_Nullable error) {
|
|
401
|
+
if (error) {
|
|
402
|
+
reject(@"PIV_ERROR", error.localizedDescription, error);
|
|
403
|
+
return;
|
|
404
|
+
}
|
|
405
|
+
resolve(nil);
|
|
406
|
+
}];
|
|
407
|
+
}];
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
- (void)unblockPin:(NSString *)deviceHandle
|
|
411
|
+
puk:(NSString *)puk
|
|
412
|
+
newPin:(NSString *)newPin
|
|
413
|
+
resolve:(RCTPromiseResolveBlock)resolve
|
|
414
|
+
reject:(RCTPromiseRejectBlock)reject {
|
|
415
|
+
id<YKFConnectionProtocol> connection = [self connectionForHandle:deviceHandle reject:reject];
|
|
416
|
+
if (!connection) return;
|
|
417
|
+
|
|
418
|
+
[connection pivSession:^(YKFPIVSession *_Nullable session, NSError *_Nullable error) {
|
|
419
|
+
if (error || session == nil) {
|
|
420
|
+
reject(@"PIV_ERROR", error ? error.localizedDescription : @"PIV session not available", error);
|
|
421
|
+
return;
|
|
422
|
+
}
|
|
423
|
+
[session unblockPinWithPuk:puk newPin:newPin completion:^(NSError *_Nullable error) {
|
|
424
|
+
if (error) {
|
|
425
|
+
reject(@"PIV_ERROR", error.localizedDescription, error);
|
|
426
|
+
return;
|
|
427
|
+
}
|
|
428
|
+
resolve(nil);
|
|
429
|
+
}];
|
|
430
|
+
}];
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
- (void)setPinAttempts:(NSString *)deviceHandle
|
|
434
|
+
pinAttempts:(double)pinAttempts
|
|
435
|
+
pukAttempts:(double)pukAttempts
|
|
436
|
+
resolve:(RCTPromiseResolveBlock)resolve
|
|
437
|
+
reject:(RCTPromiseRejectBlock)reject {
|
|
438
|
+
id<YKFConnectionProtocol> connection = [self connectionForHandle:deviceHandle reject:reject];
|
|
439
|
+
if (!connection) return;
|
|
440
|
+
|
|
441
|
+
[connection pivSession:^(YKFPIVSession *_Nullable session, NSError *_Nullable error) {
|
|
442
|
+
if (error || session == nil) {
|
|
443
|
+
reject(@"PIV_ERROR", error ? error.localizedDescription : @"PIV session not available", error);
|
|
444
|
+
return;
|
|
445
|
+
}
|
|
446
|
+
[session setPinAttempts:(int)pinAttempts
|
|
447
|
+
pukAttempts:(int)pukAttempts
|
|
448
|
+
completion:^(NSError *_Nullable error) {
|
|
449
|
+
if (error) {
|
|
450
|
+
reject(@"PIV_ERROR", error.localizedDescription, error);
|
|
451
|
+
return;
|
|
452
|
+
}
|
|
453
|
+
resolve(nil);
|
|
454
|
+
}];
|
|
455
|
+
}];
|
|
456
|
+
}
|
|
457
|
+
|
|
458
|
+
- (void)getPinMetadata:(NSString *)deviceHandle
|
|
459
|
+
resolve:(RCTPromiseResolveBlock)resolve
|
|
460
|
+
reject:(RCTPromiseRejectBlock)reject {
|
|
461
|
+
id<YKFConnectionProtocol> connection = [self connectionForHandle:deviceHandle reject:reject];
|
|
462
|
+
if (!connection) return;
|
|
463
|
+
|
|
464
|
+
[connection pivSession:^(YKFPIVSession *_Nullable session, NSError *_Nullable error) {
|
|
465
|
+
if (error || session == nil) {
|
|
466
|
+
reject(@"PIV_ERROR", error ? error.localizedDescription : @"PIV session not available", error);
|
|
467
|
+
return;
|
|
468
|
+
}
|
|
469
|
+
[session getPinMetadataWithCompletion:^(bool isDefault, int retriesTotal, int retriesRemaining, NSError *_Nullable error) {
|
|
470
|
+
if (error) {
|
|
471
|
+
reject(@"PIV_ERROR", error.localizedDescription, error);
|
|
472
|
+
return;
|
|
473
|
+
}
|
|
474
|
+
resolve(@{@"attemptsRemaining": @(retriesRemaining)});
|
|
475
|
+
}];
|
|
476
|
+
}];
|
|
477
|
+
}
|
|
478
|
+
|
|
479
|
+
- (void)getPukMetadata:(NSString *)deviceHandle
|
|
480
|
+
resolve:(RCTPromiseResolveBlock)resolve
|
|
481
|
+
reject:(RCTPromiseRejectBlock)reject {
|
|
482
|
+
id<YKFConnectionProtocol> connection = [self connectionForHandle:deviceHandle reject:reject];
|
|
483
|
+
if (!connection) return;
|
|
484
|
+
|
|
485
|
+
[connection pivSession:^(YKFPIVSession *_Nullable session, NSError *_Nullable error) {
|
|
486
|
+
if (error || session == nil) {
|
|
487
|
+
reject(@"PIV_ERROR", error ? error.localizedDescription : @"PIV session not available", error);
|
|
488
|
+
return;
|
|
489
|
+
}
|
|
490
|
+
[session getPukMetadataWithCompletion:^(bool isDefault, int retriesTotal, int retriesRemaining, NSError *_Nullable error) {
|
|
491
|
+
if (error) {
|
|
492
|
+
reject(@"PIV_ERROR", error.localizedDescription, error);
|
|
493
|
+
return;
|
|
494
|
+
}
|
|
495
|
+
resolve(@{@"attemptsRemaining": @(retriesRemaining)});
|
|
496
|
+
}];
|
|
497
|
+
}];
|
|
498
|
+
}
|
|
499
|
+
|
|
500
|
+
- (void)getManagementKeyMetadata:(NSString *)deviceHandle
|
|
501
|
+
resolve:(RCTPromiseResolveBlock)resolve
|
|
502
|
+
reject:(RCTPromiseRejectBlock)reject {
|
|
503
|
+
id<YKFConnectionProtocol> connection = [self connectionForHandle:deviceHandle reject:reject];
|
|
504
|
+
if (!connection) return;
|
|
505
|
+
|
|
506
|
+
[connection pivSession:^(YKFPIVSession *_Nullable session, NSError *_Nullable error) {
|
|
507
|
+
if (error || session == nil) {
|
|
508
|
+
reject(@"PIV_ERROR", error ? error.localizedDescription : @"PIV session not available", error);
|
|
509
|
+
return;
|
|
510
|
+
}
|
|
511
|
+
[session getManagementKeyMetadataWithCompletion:^(YKFPIVManagementKeyMetadata *_Nullable metaData, NSError *_Nullable error) {
|
|
512
|
+
if (error || metaData == nil) {
|
|
513
|
+
reject(@"PIV_ERROR", error ? error.localizedDescription : @"Management key metadata not available", error);
|
|
514
|
+
return;
|
|
515
|
+
}
|
|
516
|
+
BOOL touchRequired = metaData.touchPolicy == YKFPIVTouchPolicyAlways || metaData.touchPolicy == YKFPIVTouchPolicyCached;
|
|
517
|
+
resolve(@{
|
|
518
|
+
@"keyType": [self stringFromManagementKeyType:metaData.keyType],
|
|
519
|
+
@"defaultValue": @(metaData.isDefault),
|
|
520
|
+
@"touchRequired": @(touchRequired)
|
|
521
|
+
});
|
|
522
|
+
}];
|
|
523
|
+
}];
|
|
524
|
+
}
|
|
525
|
+
|
|
526
|
+
- (void)getSlotMetadata:(NSString *)deviceHandle
|
|
527
|
+
slot:(NSString *)slot
|
|
528
|
+
resolve:(RCTPromiseResolveBlock)resolve
|
|
529
|
+
reject:(RCTPromiseRejectBlock)reject {
|
|
530
|
+
id<YKFConnectionProtocol> connection = [self connectionForHandle:deviceHandle reject:reject];
|
|
531
|
+
if (!connection) return;
|
|
532
|
+
|
|
533
|
+
[connection pivSession:^(YKFPIVSession *_Nullable session, NSError *_Nullable error) {
|
|
534
|
+
if (error || session == nil) {
|
|
535
|
+
reject(@"PIV_ERROR", error ? error.localizedDescription : @"PIV session not available", error);
|
|
536
|
+
return;
|
|
537
|
+
}
|
|
538
|
+
[session getMetadataForSlot:[self slotFromString:slot]
|
|
539
|
+
completion:^(YKFPIVSlotMetadata *_Nullable metaData, NSError *_Nullable error) {
|
|
540
|
+
if (error || metaData == nil) {
|
|
541
|
+
reject(@"PIV_ERROR", error ? error.localizedDescription : @"Slot metadata not available", error);
|
|
542
|
+
return;
|
|
543
|
+
}
|
|
544
|
+
NSMutableDictionary *result = [NSMutableDictionary dictionary];
|
|
545
|
+
result[@"keyType"] = [self stringFromKeyType:metaData.keyType];
|
|
546
|
+
result[@"pinPolicy"] = [self stringFromPinPolicy:metaData.pinPolicy];
|
|
547
|
+
result[@"touchPolicy"] = [self stringFromTouchPolicy:metaData.touchPolicy];
|
|
548
|
+
result[@"generated"] = @(metaData.generated);
|
|
549
|
+
NSString *publicKey = [self base64FromSecKey:metaData.publicKey];
|
|
550
|
+
if (publicKey != nil) {
|
|
551
|
+
result[@"publicKey"] = publicKey;
|
|
552
|
+
}
|
|
553
|
+
resolve(result);
|
|
554
|
+
}];
|
|
555
|
+
}];
|
|
556
|
+
}
|
|
557
|
+
|
|
558
|
+
- (void)getCertificate:(NSString *)deviceHandle
|
|
559
|
+
slot:(NSString *)slot
|
|
560
|
+
resolve:(RCTPromiseResolveBlock)resolve
|
|
561
|
+
reject:(RCTPromiseRejectBlock)reject {
|
|
562
|
+
id<YKFConnectionProtocol> connection = [self connectionForHandle:deviceHandle reject:reject];
|
|
563
|
+
if (!connection) return;
|
|
564
|
+
|
|
565
|
+
[connection pivSession:^(YKFPIVSession *_Nullable session, NSError *_Nullable error) {
|
|
566
|
+
if (error || session == nil) {
|
|
567
|
+
reject(@"PIV_ERROR", error ? error.localizedDescription : @"PIV session not available", error);
|
|
568
|
+
return;
|
|
569
|
+
}
|
|
570
|
+
[session getCertificateInSlot:[self slotFromString:slot]
|
|
571
|
+
completion:^(SecCertificateRef _Nullable certificate, NSError *_Nullable error) {
|
|
572
|
+
if (error || certificate == NULL) {
|
|
573
|
+
reject(@"PIV_ERROR", error ? error.localizedDescription : @"No certificate in slot", error);
|
|
574
|
+
return;
|
|
575
|
+
}
|
|
576
|
+
resolve([self base64FromSecCertificate:certificate]);
|
|
577
|
+
}];
|
|
578
|
+
}];
|
|
579
|
+
}
|
|
580
|
+
|
|
581
|
+
- (void)putCertificate:(NSString *)deviceHandle
|
|
582
|
+
slot:(NSString *)slot
|
|
583
|
+
certificate:(NSString *)certificate
|
|
584
|
+
compress:(NSNumber *)compress
|
|
585
|
+
resolve:(RCTPromiseResolveBlock)resolve
|
|
586
|
+
reject:(RCTPromiseRejectBlock)reject {
|
|
587
|
+
id<YKFConnectionProtocol> connection = [self connectionForHandle:deviceHandle reject:reject];
|
|
588
|
+
if (!connection) return;
|
|
589
|
+
|
|
590
|
+
NSData *certData = [[NSData alloc] initWithBase64EncodedString:certificate options:0];
|
|
591
|
+
if (certData == nil) {
|
|
592
|
+
reject(@"PIV_ERROR", @"Invalid base64 certificate", nil);
|
|
593
|
+
return;
|
|
594
|
+
}
|
|
595
|
+
|
|
596
|
+
SecCertificateRef certRef = SecCertificateCreateWithData(NULL, (__bridge CFDataRef)certData);
|
|
597
|
+
if (certRef == NULL) {
|
|
598
|
+
reject(@"PIV_ERROR", @"Invalid certificate data", nil);
|
|
599
|
+
return;
|
|
600
|
+
}
|
|
601
|
+
|
|
602
|
+
[connection pivSession:^(YKFPIVSession *_Nullable session, NSError *_Nullable error) {
|
|
603
|
+
if (error || session == nil) {
|
|
604
|
+
reject(@"PIV_ERROR", error ? error.localizedDescription : @"PIV session not available", error);
|
|
605
|
+
CFRelease(certRef);
|
|
606
|
+
return;
|
|
607
|
+
}
|
|
608
|
+
[session putCertificate:certRef
|
|
609
|
+
inSlot:[self slotFromString:slot]
|
|
610
|
+
compress:compress != nil && compress.boolValue
|
|
611
|
+
completion:^(NSError *_Nullable error) {
|
|
612
|
+
if (error) {
|
|
613
|
+
reject(@"PIV_ERROR", error.localizedDescription, error);
|
|
614
|
+
return;
|
|
615
|
+
}
|
|
616
|
+
resolve(nil);
|
|
617
|
+
}];
|
|
618
|
+
CFRelease(certRef);
|
|
619
|
+
}];
|
|
620
|
+
}
|
|
621
|
+
|
|
622
|
+
- (void)deleteCertificate:(NSString *)deviceHandle
|
|
623
|
+
slot:(NSString *)slot
|
|
624
|
+
resolve:(RCTPromiseResolveBlock)resolve
|
|
625
|
+
reject:(RCTPromiseRejectBlock)reject {
|
|
626
|
+
id<YKFConnectionProtocol> connection = [self connectionForHandle:deviceHandle reject:reject];
|
|
627
|
+
if (!connection) return;
|
|
628
|
+
|
|
629
|
+
[connection pivSession:^(YKFPIVSession *_Nullable session, NSError *_Nullable error) {
|
|
630
|
+
if (error || session == nil) {
|
|
631
|
+
reject(@"PIV_ERROR", error ? error.localizedDescription : @"PIV session not available", error);
|
|
632
|
+
return;
|
|
633
|
+
}
|
|
634
|
+
[session deleteCertificateInSlot:[self slotFromString:slot] completion:^(NSError *_Nullable error) {
|
|
635
|
+
if (error) {
|
|
636
|
+
reject(@"PIV_ERROR", error.localizedDescription, error);
|
|
637
|
+
return;
|
|
638
|
+
}
|
|
639
|
+
resolve(nil);
|
|
640
|
+
}];
|
|
641
|
+
}];
|
|
642
|
+
}
|
|
643
|
+
|
|
644
|
+
- (void)attestKey:(NSString *)deviceHandle
|
|
645
|
+
slot:(NSString *)slot
|
|
646
|
+
resolve:(RCTPromiseResolveBlock)resolve
|
|
647
|
+
reject:(RCTPromiseRejectBlock)reject {
|
|
648
|
+
id<YKFConnectionProtocol> connection = [self connectionForHandle:deviceHandle reject:reject];
|
|
649
|
+
if (!connection) return;
|
|
650
|
+
|
|
651
|
+
[connection pivSession:^(YKFPIVSession *_Nullable session, NSError *_Nullable error) {
|
|
652
|
+
if (error || session == nil) {
|
|
653
|
+
reject(@"PIV_ERROR", error ? error.localizedDescription : @"PIV session not available", error);
|
|
654
|
+
return;
|
|
655
|
+
}
|
|
656
|
+
[session attestKeyInSlot:[self slotFromString:slot]
|
|
657
|
+
completion:^(SecCertificateRef _Nullable certificate, NSError *_Nullable error) {
|
|
658
|
+
if (error || certificate == NULL) {
|
|
659
|
+
reject(@"PIV_ERROR", error ? error.localizedDescription : @"Attestation failed", error);
|
|
660
|
+
return;
|
|
661
|
+
}
|
|
662
|
+
resolve([self base64FromSecCertificate:certificate]);
|
|
663
|
+
}];
|
|
664
|
+
}];
|
|
665
|
+
}
|
|
666
|
+
|
|
667
|
+
- (void)generateKey:(NSString *)deviceHandle
|
|
668
|
+
slot:(NSString *)slot
|
|
669
|
+
keyType:(NSString *)keyType
|
|
670
|
+
pinPolicy:(NSString *)pinPolicy
|
|
671
|
+
touchPolicy:(NSString *)touchPolicy
|
|
672
|
+
resolve:(RCTPromiseResolveBlock)resolve
|
|
673
|
+
reject:(RCTPromiseRejectBlock)reject {
|
|
674
|
+
id<YKFConnectionProtocol> connection = [self connectionForHandle:deviceHandle reject:reject];
|
|
675
|
+
if (!connection) return;
|
|
676
|
+
|
|
677
|
+
[connection pivSession:^(YKFPIVSession *_Nullable session, NSError *_Nullable error) {
|
|
678
|
+
if (error || session == nil) {
|
|
679
|
+
reject(@"PIV_ERROR", error ? error.localizedDescription : @"PIV session not available", error);
|
|
680
|
+
return;
|
|
681
|
+
}
|
|
682
|
+
[session generateKeyInSlot:[self slotFromString:slot]
|
|
683
|
+
type:[self keyTypeFromString:keyType]
|
|
684
|
+
pinPolicy:[self pinPolicyFromString:pinPolicy]
|
|
685
|
+
touchPolicy:[self touchPolicyFromString:touchPolicy]
|
|
686
|
+
completion:^(SecKeyRef _Nullable key, NSError *_Nullable error) {
|
|
687
|
+
if (error || key == NULL) {
|
|
688
|
+
reject(@"PIV_ERROR", error ? error.localizedDescription : @"Key generation failed", error);
|
|
689
|
+
return;
|
|
690
|
+
}
|
|
691
|
+
resolve([self base64FromSecKey:key]);
|
|
692
|
+
}];
|
|
693
|
+
}];
|
|
694
|
+
}
|
|
695
|
+
|
|
696
|
+
- (void)deleteKey:(NSString *)deviceHandle
|
|
697
|
+
slot:(NSString *)slot
|
|
698
|
+
resolve:(RCTPromiseResolveBlock)resolve
|
|
699
|
+
reject:(RCTPromiseRejectBlock)reject {
|
|
700
|
+
id<YKFConnectionProtocol> connection = [self connectionForHandle:deviceHandle reject:reject];
|
|
701
|
+
if (!connection) return;
|
|
702
|
+
|
|
703
|
+
[connection pivSession:^(YKFPIVSession *_Nullable session, NSError *_Nullable error) {
|
|
704
|
+
if (error || session == nil) {
|
|
705
|
+
reject(@"PIV_ERROR", error ? error.localizedDescription : @"PIV session not available", error);
|
|
706
|
+
return;
|
|
707
|
+
}
|
|
708
|
+
[session deleteKeyInSlot:[self slotFromString:slot] completion:^(NSError *_Nullable error) {
|
|
709
|
+
if (error) {
|
|
710
|
+
reject(@"PIV_ERROR", error.localizedDescription, error);
|
|
711
|
+
return;
|
|
712
|
+
}
|
|
713
|
+
resolve(nil);
|
|
714
|
+
}];
|
|
715
|
+
}];
|
|
716
|
+
}
|
|
717
|
+
|
|
718
|
+
- (void)rawSignOrDecrypt:(NSString *)deviceHandle
|
|
719
|
+
slot:(NSString *)slot
|
|
720
|
+
keyType:(NSString *)keyType
|
|
721
|
+
payload:(NSString *)payload
|
|
722
|
+
resolve:(RCTPromiseResolveBlock)resolve
|
|
723
|
+
reject:(RCTPromiseRejectBlock)reject {
|
|
724
|
+
id<YKFConnectionProtocol> connection = [self connectionForHandle:deviceHandle reject:reject];
|
|
725
|
+
if (!connection) return;
|
|
726
|
+
|
|
727
|
+
NSData *payloadData = [[NSData alloc] initWithBase64EncodedString:payload options:0];
|
|
728
|
+
if (payloadData == nil) {
|
|
729
|
+
reject(@"PIV_ERROR", @"Invalid base64 payload", nil);
|
|
730
|
+
return;
|
|
731
|
+
}
|
|
732
|
+
|
|
733
|
+
YKFPIVSlot pivSlot = [self slotFromString:slot];
|
|
734
|
+
YKFPIVKeyType pivKeyType = [self keyTypeFromString:keyType];
|
|
735
|
+
|
|
736
|
+
// YKFPIVPadding (used internally by signWithKeyInSlot:/decryptWithKeyInSlot:) only
|
|
737
|
+
// handles RSA1024/RSA2048 — RSA3072/RSA4096 keys can be generated on iOS but can't
|
|
738
|
+
// be used for raw sign/decrypt via this SDK version, so fail fast with a clear
|
|
739
|
+
// message instead of a cryptic Security-framework error.
|
|
740
|
+
if (pivKeyType == YKFPIVKeyTypeRSA3072 || pivKeyType == YKFPIVKeyTypeRSA4096) {
|
|
741
|
+
reject(@"PIV_ERROR", @"rawSignOrDecrypt for RSA3072/RSA4096 is not supported by the YubiKit iOS SDK", nil);
|
|
742
|
+
return;
|
|
743
|
+
}
|
|
744
|
+
|
|
745
|
+
NSError *normalizeError = nil;
|
|
746
|
+
NSData *normalizedPayload = [self normalizedPayload:payloadData forKeyType:pivKeyType error:&normalizeError];
|
|
747
|
+
if (normalizedPayload == nil) {
|
|
748
|
+
reject(@"PIV_ERROR", normalizeError ? normalizeError.localizedDescription : @"Invalid payload for key type", normalizeError);
|
|
749
|
+
return;
|
|
750
|
+
}
|
|
751
|
+
|
|
752
|
+
[connection pivSession:^(YKFPIVSession *_Nullable session, NSError *_Nullable error) {
|
|
753
|
+
if (error || session == nil) {
|
|
754
|
+
reject(@"PIV_ERROR", error ? error.localizedDescription : @"PIV session not available", error);
|
|
755
|
+
return;
|
|
756
|
+
}
|
|
757
|
+
|
|
758
|
+
if ([self isDecryptOperationForSlot:pivSlot keyType:pivKeyType]) {
|
|
759
|
+
[session decryptWithKeyInSlot:pivSlot
|
|
760
|
+
algorithm:kSecKeyAlgorithmRSAEncryptionRaw
|
|
761
|
+
encrypted:normalizedPayload
|
|
762
|
+
completion:^(NSData *_Nullable decrypted, NSError *_Nullable error) {
|
|
763
|
+
if (error || decrypted == nil) {
|
|
764
|
+
reject(@"PIV_ERROR", error ? error.localizedDescription : @"Decryption failed", error);
|
|
765
|
+
return;
|
|
766
|
+
}
|
|
767
|
+
resolve([decrypted base64EncodedStringWithOptions:0]);
|
|
768
|
+
}];
|
|
769
|
+
} else {
|
|
770
|
+
[session signWithKeyInSlot:pivSlot
|
|
771
|
+
type:pivKeyType
|
|
772
|
+
algorithm:[self signAlgorithmForKeyType:pivKeyType]
|
|
773
|
+
message:normalizedPayload
|
|
774
|
+
completion:^(NSData *_Nullable signature, NSError *_Nullable error) {
|
|
775
|
+
if (error || signature == nil) {
|
|
776
|
+
reject(@"PIV_ERROR", error ? error.localizedDescription : @"Signing failed", error);
|
|
777
|
+
return;
|
|
778
|
+
}
|
|
779
|
+
resolve([signature base64EncodedStringWithOptions:0]);
|
|
780
|
+
}];
|
|
781
|
+
}
|
|
782
|
+
}];
|
|
783
|
+
}
|
|
784
|
+
|
|
785
|
+
- (void)getBioMetadata:(NSString *)deviceHandle
|
|
786
|
+
resolve:(RCTPromiseResolveBlock)resolve
|
|
787
|
+
reject:(RCTPromiseRejectBlock)reject {
|
|
788
|
+
id<YKFConnectionProtocol> connection = [self connectionForHandle:deviceHandle reject:reject];
|
|
789
|
+
if (!connection) return;
|
|
790
|
+
|
|
791
|
+
[connection pivSession:^(YKFPIVSession *_Nullable session, NSError *_Nullable error) {
|
|
792
|
+
if (error || session == nil) {
|
|
793
|
+
reject(@"PIV_ERROR", error ? error.localizedDescription : @"PIV session not available", error);
|
|
794
|
+
return;
|
|
795
|
+
}
|
|
796
|
+
[session getBioMetadataWithCompletion:^(YKFPIVBioMetadata *_Nullable metaData, NSError *_Nullable error) {
|
|
797
|
+
if (error || metaData == nil) {
|
|
798
|
+
reject(@"PIV_ERROR", error ? error.localizedDescription : @"Bio metadata not available", error);
|
|
799
|
+
return;
|
|
800
|
+
}
|
|
801
|
+
resolve(@{
|
|
802
|
+
@"attemptsRemaining": @(metaData.attemptsRemaining),
|
|
803
|
+
@"temporaryPin": @(metaData.temporaryPin)
|
|
804
|
+
});
|
|
805
|
+
}];
|
|
806
|
+
}];
|
|
807
|
+
}
|
|
808
|
+
|
|
809
|
+
- (std::shared_ptr<facebook::react::TurboModule>)getTurboModule:(const facebook::react::ObjCTurboModule::InitParams &)params {
|
|
810
|
+
return std::make_shared<facebook::react::NativeYubikitPivSpecJSI>(params);
|
|
811
|
+
}
|
|
812
|
+
|
|
813
|
+
@end
|