Skip to content

Commit e559471

Browse files
committed
add support for multiple on-card-applications
fixes OpenSC/OpenSC#2986
1 parent f860cab commit e559471

File tree

3 files changed

+162
-124
lines changed

3 files changed

+162
-124
lines changed

OpenSCToken/Token.h

Lines changed: 23 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -22,33 +22,43 @@
2222

2323
#include "libopensc/pkcs15.h"
2424

25+
/* in sync with SC_PKCS11_FRAMEWORK_DATA_MAX_NUM */
26+
#define SC_TOKEN_APPS_MAX_NUM 4
27+
struct token_apps {
28+
struct sc_pkcs15_card * _Nullable p15card[SC_TOKEN_APPS_MAX_NUM];
29+
};
30+
2531
#define TYPE_CERT 0x01
2632
#define TYPE_PRIV 0x02
2733
#define TYPE_AUTH 0x03
2834

29-
static NSData* _Nullable idToData(u8 type, struct sc_pkcs15_id * _Nullable p15id)
35+
/* The converted data consists of app_index+type+p15id. This allows identifying which on-card-application has this object if there are multiple applications. And this allows differentiating different types of objects with the same p15id. */
36+
static NSData* _Nullable idToData(u8 index, u8 type, struct sc_pkcs15_id * _Nullable p15id)
3037
{
3138
NSData *data = nil;
3239
if (p15id) {
33-
u8 *p = malloc(p15id->len+1);
40+
u8 *p = malloc(p15id->len+2);
3441
if (p) {
35-
*p = type;
36-
memcpy(p+1, p15id->value, p15id->len);
37-
data = [NSData dataWithBytes:p length:p15id->len+1];
42+
p[0] = index;
43+
p[1] = type;
44+
memcpy(&p[2], p15id->value, p15id->len);
45+
data = [NSData dataWithBytes:p length:p15id->len+2];
3846
free(p);
3947
}
4048
}
4149
return data;
4250
}
4351

44-
static struct sc_pkcs15_id dataToId(NSData* _Nonnull data)
52+
static struct sc_pkcs15_id dataToId(NSData* _Nonnull data, u8 * _Nullable index)
4553
{
46-
struct sc_pkcs15_id p15id;
47-
p15id.len = [data length];
48-
memcpy(p15id.value, [data bytes], p15id.len);
49-
if (p15id.len > 0) {
50-
p15id.len--;
51-
memmove(p15id.value, p15id.value+1, p15id.len);
54+
struct sc_pkcs15_id p15id = {0};
55+
size_t data_len = [data length];
56+
const unsigned char *p = [data bytes];
57+
if (data_len > 1 && p) {
58+
if (index)
59+
*index = p[0];
60+
memcpy(p15id.value, &p[2], data_len-2);
61+
p15id.len = data_len-2;
5262
}
5363
return p15id;
5464
}
@@ -78,7 +88,7 @@ NS_ASSUME_NONNULL_BEGIN
7888
@property (readonly) OpenSCTokenDriver *driver;
7989
@property (nonatomic, assign, nullable) struct sc_context *ctx;
8090
@property (nonatomic, assign, nullable) struct sc_card *card;
81-
@property (nonatomic, assign, nullable) struct sc_pkcs15_card *p15card;
91+
@property (nonatomic, assign) struct token_apps apps;
8292

8393
@end
8494

OpenSCToken/Token.m

Lines changed: 113 additions & 93 deletions
Original file line numberDiff line numberDiff line change
@@ -58,15 +58,20 @@ - (nullable instancetype)initWithSmartCard:(TKSmartCard *)smartCard AID:(nullabl
5858
struct sc_aid *aid = NULL;
5959
struct sc_pkcs15_object *objs[32];
6060
int r;
61-
size_t i, cert_num;
62-
NSMutableArray<TKTokenKeychainItem *> *items;
61+
size_t i, app_index, cert_num;
62+
NSMutableArray<TKTokenKeychainItem *> *items = [[NSMutableArray alloc] init];
6363
NSString *instanceID = nil;
64+
struct token_apps apps;
6465

6566
/* TODO: Move card and p15card to smartCard.context. smartCard.context would automatically be set to nil if the card gets reset or the state gets modified by a different session, see documentation for TKSmartCard */
6667

6768
self.card = NULL;
68-
self.p15card = NULL;
6969
self.ctx = NULL;
70+
/* self.apps.p15card allows binding multiple on-card-application. index 0 always contains the "generic" app. */
71+
for (app_index = 0; app_index < SC_TOKEN_APPS_MAX_NUM; app_index++) {
72+
self.apps.p15card[app_index] = NULL;
73+
apps.p15card[app_index] = NULL;
74+
}
7075

7176
os_log(OS_LOG_DEFAULT, "%s", OPENSC_SCM_REVISION);
7277

@@ -105,110 +110,123 @@ - (nullable instancetype)initWithSmartCard:(TKSmartCard *)smartCard AID:(nullabl
105110

106111
app_generic = sc_pkcs15_get_application_by_type(card, "generic");
107112
aid = app_generic ? &app_generic->aid : NULL;
108-
r = sc_pkcs15_bind(card, aid, &p15card);
109-
LOG_TEST_GOTO_ERR(ctx, r, "sc_pkcs15_bind");
110113

111-
r = sc_pkcs15_get_objects(p15card, SC_PKCS15_TYPE_CERT_X509, objs, sizeof(objs)/(sizeof *objs));
112-
LOG_TEST_GOTO_ERR(ctx, r, "sc_pkcs15_get_objects (SC_PKCS15_TYPE_CERT_X509)");
113-
cert_num = (size_t) r;
114-
items = [NSMutableArray arrayWithCapacity:cert_num];
115-
for (i = 0; i < cert_num; i++) {
116-
struct sc_pkcs15_cert_info *cert_info = objs[i]->data;
117-
struct sc_pkcs15_cert *cert = NULL;
118-
struct sc_pkcs15_object *prkey_obj = NULL;
119-
int private_obj = objs[i]->flags & SC_PKCS15_CO_FLAG_PRIVATE;
120-
121-
r = sc_pkcs15_read_certificate(p15card, cert_info, private_obj, &cert);
122-
if (r) {
123-
sc_log(ctx, "sc_pkcs15_read_certificate: %s", sc_strerror(r));
124-
continue;
125-
}
126-
NSData* certificateData = [NSData dataWithBytes:(const void *)cert->data.value length:sizeof(unsigned char)*cert->data.len];
127-
NSData* certificateID = idToData(TYPE_CERT, &cert_info->id);
128-
NSString *certificateName = [NSString stringWithUTF8String:objs[i]->label];
129-
id certificate = CFBridgingRelease(SecCertificateCreateWithData(kCFAllocatorDefault, (CFDataRef)certificateData));
130-
if (certificateData == nil || certificateID == nil || certificateName == nil || certificate == NULL) {
131-
sc_pkcs15_free_certificate(cert);
132-
continue;
133-
}
134-
TKTokenKeychainCertificate *certificateItem = [[TKTokenKeychainCertificate alloc] initWithCertificate:(__bridge SecCertificateRef)certificate objectID:certificateID];
135-
if (certificateItem == nil) {
114+
/* Bind all applications starting with the "generic" one */
115+
for (app_index = 0; app_index < SC_TOKEN_APPS_MAX_NUM; app_index++) {
116+
r = sc_pkcs15_bind(card, aid, &p15card);
117+
apps.p15card[app_index] = p15card;
118+
LOG_TEST_GOTO_ERR(ctx, r, "sc_pkcs15_bind");
119+
120+
r = sc_pkcs15_get_objects(p15card, SC_PKCS15_TYPE_CERT_X509, objs, sizeof(objs)/(sizeof *objs));
121+
LOG_TEST_GOTO_ERR(ctx, r, "sc_pkcs15_get_objects (SC_PKCS15_TYPE_CERT_X509)");
122+
cert_num = (size_t) r;
123+
for (i = 0; i < cert_num; i++) {
124+
struct sc_pkcs15_cert_info *cert_info = objs[i]->data;
125+
struct sc_pkcs15_cert *cert = NULL;
126+
struct sc_pkcs15_object *prkey_obj = NULL;
127+
int private_obj = objs[i]->flags & SC_PKCS15_CO_FLAG_PRIVATE;
128+
129+
r = sc_pkcs15_read_certificate(p15card, cert_info, private_obj, &cert);
130+
if (r) {
131+
sc_log(ctx, "sc_pkcs15_read_certificate: %s", sc_strerror(r));
132+
continue;
133+
}
134+
NSData* certificateData = [NSData dataWithBytes:(const void *)cert->data.value length:sizeof(unsigned char)*cert->data.len];
135+
NSData* certificateID = idToData(app_index, TYPE_CERT, &cert_info->id);
136+
NSString *certificateName = [NSString stringWithUTF8String:objs[i]->label];
137+
id certificate = CFBridgingRelease(SecCertificateCreateWithData(kCFAllocatorDefault, (CFDataRef)certificateData));
138+
if (certificateData == nil || certificateID == nil || certificateName == nil || certificate == NULL) {
139+
sc_pkcs15_free_certificate(cert);
140+
continue;
141+
}
142+
TKTokenKeychainCertificate *certificateItem = [[TKTokenKeychainCertificate alloc] initWithCertificate:(__bridge SecCertificateRef)certificate objectID:certificateID];
143+
if (certificateItem == nil) {
144+
sc_pkcs15_free_certificate(cert);
145+
continue;
146+
}
147+
[certificateItem setName:certificateName];
148+
[items addObject:certificateItem];
136149
sc_pkcs15_free_certificate(cert);
137-
continue;
150+
151+
// Create key item.
152+
r = sc_pkcs15_find_prkey_by_id(p15card, &cert_info->id, &prkey_obj);
153+
if (r) {
154+
sc_log(ctx, "sc_pkcs15_find_prkey_by_id: %s", sc_strerror(r));
155+
continue;
156+
}
157+
struct sc_pkcs15_prkey_info *prkey_info = (struct sc_pkcs15_prkey_info *) prkey_obj->data;
158+
NSData* keyID = idToData(app_index, TYPE_PRIV, &prkey_info->id);
159+
NSString *keyName = [NSString stringWithUTF8String:objs[i]->label];
160+
TKTokenKeychainKey *keyItem = [[TKTokenKeychainKey alloc] initWithCertificate:(__bridge SecCertificateRef)certificate objectID:keyID];
161+
if (keyID == nil || keyName == nil || keyItem == nil) {
162+
continue;
163+
}
164+
[keyItem setName:keyName];
165+
166+
NSMutableDictionary<NSNumber *, TKTokenOperationConstraint> *constraints = [NSMutableDictionary dictionary];
167+
TKTokenOperationConstraint constraint;
168+
if (prkey_obj->auth_id.len == 0) {
169+
/* true, indicating that the operation is always allowed, without any authentication necessary. */
170+
constraint = @YES;
171+
} else {
172+
/* Any other property list compatible value defined by the implementation of the token extension. Any such constraint is required to stay constant for the entire lifetime of the token. */
173+
constraint = idToData(app_index, TYPE_AUTH, &prkey_obj->auth_id);
174+
}
175+
176+
if (USAGE_ANY_SIGN & prkey_info->usage) {
177+
keyItem.canSign = YES;
178+
constraints[@(TKTokenOperationSignData)] = constraint;
179+
} else {
180+
keyItem.canSign = NO;
181+
}
182+
keyItem.suitableForLogin = keyItem.canSign;
183+
184+
if (USAGE_ANY_DECIPHER & prkey_info->usage) {
185+
keyItem.canDecrypt = YES;
186+
constraints[@(TKTokenOperationDecryptData)] = constraint;
187+
} else {
188+
keyItem.canDecrypt = NO;
189+
}
190+
191+
if (USAGE_ANY_AGREEMENT & prkey_info->usage) {
192+
keyItem.canPerformKeyExchange = YES;
193+
constraints[@(TKTokenOperationPerformKeyExchange)] = constraint;
194+
} else {
195+
keyItem.canPerformKeyExchange = NO;
196+
}
197+
198+
keyItem.constraints = constraints;
199+
[items addObject:keyItem];
138200
}
139-
[certificateItem setName:certificateName];
140-
[items addObject:certificateItem];
141-
sc_pkcs15_free_certificate(cert);
142-
143-
// Create key item.
144-
r = sc_pkcs15_find_prkey_by_id(p15card, &cert_info->id, &prkey_obj);
145-
if (r) {
146-
sc_log(ctx, "sc_pkcs15_find_prkey_by_id: %s", sc_strerror(r));
147-
continue;
148-
}
149-
struct sc_pkcs15_prkey_info *prkey_info = (struct sc_pkcs15_prkey_info *) prkey_obj->data;
150-
NSData* keyID = idToData(TYPE_PRIV, &prkey_info->id);
151-
NSString *keyName = [NSString stringWithUTF8String:objs[i]->label];
152-
TKTokenKeychainKey *keyItem = [[TKTokenKeychainKey alloc] initWithCertificate:(__bridge SecCertificateRef)certificate objectID:keyID];
153-
if (keyID == nil || keyName == nil || keyItem == nil) {
154-
continue;
155-
}
156-
[keyItem setName:keyName];
157-
158-
NSMutableDictionary<NSNumber *, TKTokenOperationConstraint> *constraints = [NSMutableDictionary dictionary];
159-
TKTokenOperationConstraint constraint;
160-
if (prkey_obj->auth_id.len == 0) {
161-
/* true, indicating that the operation is always allowed, without any authentication necessary. */
162-
constraint = @YES;
163-
} else {
164-
/* Any other property list compatible value defined by the implementation of the token extension. Any such constraint is required to stay constant for the entire lifetime of the token. */
165-
constraint = idToData(TYPE_AUTH, &prkey_obj->auth_id);
166-
}
167-
168-
if (USAGE_ANY_SIGN & prkey_info->usage) {
169-
keyItem.canSign = YES;
170-
constraints[@(TKTokenOperationSignData)] = constraint;
171-
} else {
172-
keyItem.canSign = NO;
173-
}
174-
keyItem.suitableForLogin = keyItem.canSign;
175201

176-
if (USAGE_ANY_DECIPHER & prkey_info->usage) {
177-
keyItem.canDecrypt = YES;
178-
constraints[@(TKTokenOperationDecryptData)] = constraint;
179-
} else {
180-
keyItem.canDecrypt = NO;
181-
}
202+
/* some tokens do not report a serial number */
203+
if (p15card->tokeninfo != NULL && p15card->tokeninfo->serial_number != NULL)
204+
instanceID = [NSString stringWithUTF8String:p15card->tokeninfo->serial_number];
205+
p15card->opts.use_pin_cache = 0;
182206

183-
if (USAGE_ANY_AGREEMENT & prkey_info->usage) {
184-
keyItem.canPerformKeyExchange = YES;
185-
constraints[@(TKTokenOperationPerformKeyExchange)] = constraint;
186-
} else {
187-
keyItem.canPerformKeyExchange = NO;
188-
}
189-
190-
keyItem.constraints = constraints;
191-
[items addObject:keyItem];
207+
/* find the next application. Note that the "generic" application is at index 0, which means that the apps from card->app are taking the **subsequent** slots */
208+
if (app_index >= card->app_count)
209+
break;
210+
aid = &card->app[app_index]->aid;
211+
app_index++;
212+
/* go to the beginning of the loop and try to bind the next application */
213+
p15card = NULL;
192214
}
193-
194-
/* some tokens do not report a serial number */
195-
if (p15card->tokeninfo != NULL && p15card->tokeninfo->serial_number != NULL)
196-
instanceID = [NSString stringWithUTF8String:p15card->tokeninfo->serial_number];
197-
p15card->opts.use_pin_cache = 0;
198215

199216
if (self = [super initWithSmartCard:smartCard AID:AID instanceID:instanceID tokenDriver:tokenDriver]) {
200217
[self.keychainContents fillWithItems:items];
201-
202218
self.ctx = ctx;
203219
self.card = card;
204-
self.p15card = p15card;
220+
self.apps = apps;
205221
} else {
206222
goto err;
207223
}
208224

209225
err:
210-
if (!self.p15card && p15card)
211-
sc_pkcs15_card_free(p15card);
226+
for (app_index = 0; app_index < SC_TOKEN_APPS_MAX_NUM; app_index++) {
227+
if (!self.apps.p15card[app_index] && apps.p15card[app_index])
228+
sc_pkcs15_card_free(apps.p15card[app_index]);
229+
}
212230
if (!self.card && card)
213231
sc_disconnect_card(card);
214232
if (!self.ctx && ctx)
@@ -217,11 +235,13 @@ - (nullable instancetype)initWithSmartCard:(TKSmartCard *)smartCard AID:(nullabl
217235
}
218236

219237
- (void)dealloc {
220-
sc_pkcs15_card_free(self.p15card);
238+
for (size_t app_index = 0; app_index < SC_TOKEN_APPS_MAX_NUM; app_index++) {
239+
sc_pkcs15_card_free(self.apps.p15card[app_index]);
240+
self.apps.p15card[app_index] = NULL;
241+
}
221242
sc_disconnect_card(self.card);
222243
sc_release_context(self.ctx);
223244
self.card = NULL;
224-
self.p15card = NULL;
225245
self.ctx = NULL;
226246
}
227247

0 commit comments

Comments
 (0)