Permalink
Cannot retrieve contributors at this time
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
682 lines (591 sloc)
28.9 KB
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #import <os/log.h> | |
| #import <Foundation/Foundation.h> | |
| #import <CryptoTokenKit/CryptoTokenKit.h> | |
| #import "Token.h" | |
| #import "TokenSession.h" | |
| #import <Security/SecAsn1Coder.h> | |
| @implementation BEIDAuthOperation | |
| - (instancetype)initWithSession:(BEIDTokenSession *)session { | |
| #ifdef DEBUG | |
| os_log_info(OS_LOG_DEFAULT, "BEID initWithSession called"); | |
| #endif | |
| if (self = [super init]) { | |
| _session = session; | |
| self.smartCard = session.smartCard; | |
| self.session.authState=BEIDAuthStateUnauthorized; | |
| // TODO: use template when apple implements it | |
| /* | |
| self.smartCard.cla = 0x00; | |
| self.smartCard.useCommandChaining = NO; | |
| self.smartCard.useExtendedLength = NO; | |
| const UInt8 template[] = {self.smartCard.cla, 0x20, 0x00, 0x01, 0x08, 0x20, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}; | |
| self.APDUTemplate = [NSData dataWithBytes:template length:sizeof(template)]; | |
| self.PINFormat = [[TKSmartCardPINFormat alloc] init]; | |
| self.PINFormat.PINBlockByteLength = 8; | |
| self.PINFormat.PINLengthBitOffset = 4; | |
| self.PINFormat.PINLengthBitSize = 4; | |
| self.PINFormat.PINBitOffset = 8; | |
| self.PINFormat.encoding = TKSmartCardPINEncodingBCD; | |
| self.PINFormat.minPINLength = 4; | |
| self.PINFormat.maxPINLength = 12; | |
| self.PINFormat.charset = TKSmartCardPINCharsetNumeric; | |
| self.PINFormat.PINJustification = TKSmartCardPINJustificationLeft; | |
| */ | |
| //self.PINByteOffset=5; | |
| #ifdef DEBUG | |
| os_log_info(OS_LOG_DEFAULT, "BEID new TEST 18"); | |
| #endif | |
| BOOL (^verifySecurePIN)(NSError**) = ^(NSError** error) { | |
| TKSmartCardPINFormat *PINFormat; | |
| TKSmartCardUserInteractionForSecurePINVerification *userInter; | |
| NSData *APDUTemplate; | |
| const UInt8 template[] = {0x00, 0x20, 0x00, 0x01, 0x08, 0x20, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}; | |
| APDUTemplate = [NSData dataWithBytes:template length:sizeof(template)]; | |
| PINFormat = [[TKSmartCardPINFormat alloc] init]; | |
| PINFormat.PINBlockByteLength = 8; | |
| PINFormat.PINLengthBitOffset = 4; | |
| PINFormat.PINLengthBitSize = 4; | |
| PINFormat.PINBitOffset = 8; | |
| PINFormat.encoding = TKSmartCardPINEncodingBCD; | |
| PINFormat.minPINLength = 4; | |
| PINFormat.maxPINLength = 12; | |
| PINFormat.charset = TKSmartCardPINCharsetNumeric; | |
| PINFormat.PINJustification = TKSmartCardPINJustificationLeft; | |
| // try to Verify PIN on the card reader | |
| NSData *data = [NSData dataWithBytes:template length:sizeof template]; | |
| // using PINByteOffset:0 (in stead of 5) as requested due to not currently used | |
| //(see https://developer.apple.com/documentation/cryptotokenkit/tksmartcard/1390289-userinteractionforsecurepinverif?language=objc) | |
| userInter = [self.smartCard userInteractionForSecurePINVerificationWithPINFormat:PINFormat APDU:data PINByteOffset:0]; | |
| if (nil == userInter) | |
| { | |
| #ifdef DEBUG | |
| os_log_error(OS_LOG_DEFAULT, "userInteractionForSecurePINVerificationWithPINFormat returned nil. You are not using a pinpad reader"); | |
| #endif | |
| return NO; | |
| } | |
| else | |
| { | |
| //reader is supporting secure PIN entry | |
| dispatch_semaphore_t sema = dispatch_semaphore_create(0); | |
| NSArray *messages = @[@0];//0 is Pin insertion prompt, and only this message is needed | |
| userInter.PINMessageIndices = messages; | |
| userInter.PINCompletion = TKSmartCardPINCompletionKey;//is 2 | |
| userInter.initialTimeout = 300; | |
| userInter.interactionTimeout = 300; | |
| //using the system Locale by default | |
| //NSLocale* PINLocale = [[NSLocale alloc] initWithLocaleIdentifier:@"nl_BE"]; | |
| //userInter.locale=PINLocale; | |
| #ifdef DEBUG | |
| os_log_info(OS_LOG_DEFAULT, "Enter the PIN on the pinpad"); | |
| #endif | |
| [userInter runWithReply:^(BOOL success, NSError *error) | |
| { | |
| if (success) | |
| { | |
| #ifdef DEBUG | |
| NSLog(@"resultData: %@", [userInter resultData]); | |
| NSLog(@"resultSW: %04X", [userInter resultSW]); | |
| NSLog(@"Smartcard reader name: %@", self.session.smartCard.slot.name); | |
| #endif | |
| self.session.authState=BEIDAuthStateFreshlyAuthorized; | |
| // Mark card session sensitive, because we entered PIN into it and no session should access it in this state. | |
| self.smartCard.sensitive = YES; | |
| // Remember in card context that the card is authenticated. | |
| self.smartCard.context = @(YES); | |
| // Mark BEIDTokenSession as freshly authorized. | |
| self.session.authState = BEIDAuthStateFreshlyAuthorized; | |
| } | |
| else | |
| { | |
| #ifdef DEBUG | |
| NSLog(@"Failure enterring PIN on cardreader"); | |
| NSLog(@"Error: %@", error); | |
| #endif | |
| self.session.authState=BEIDAuthStateUnauthorized; | |
| } | |
| dispatch_semaphore_signal(sema); | |
| }]; | |
| //wait max 30 seconds for user to enter PIN on secure PIN pad reader | |
| dispatch_time_t waitTime = dispatch_time(DISPATCH_TIME_NOW, 300000000000); | |
| dispatch_semaphore_wait(sema ,waitTime); | |
| #ifdef DEBUG | |
| os_log_info(OS_LOG_DEFAULT, "PIN handled by reader"); | |
| //NSLog(@"PIN handled by reader"); | |
| #endif | |
| //some secure PIN pad readers support both secure and on PC PIN entries | |
| //when secure failed, try on PC as backup | |
| if(self.session.authState==BEIDAuthStateUnauthorized){ | |
| return NO; | |
| } | |
| return YES; | |
| } | |
| }; | |
| NSError *error ; | |
| //check if smard card reader has PIN support | |
| BOOL success = [self.smartCard inSessionWithError:(NSError **)&error executeBlock:(BOOL(^)(NSError **error))verifySecurePIN]; | |
| if (success == YES){ | |
| #ifdef DEBUG | |
| os_log_error(OS_LOG_DEFAULT, "BEID initWithSession return nil"); | |
| #endif | |
| return nil; | |
| } | |
| } | |
| #ifdef DEBUG | |
| os_log_error(OS_LOG_DEFAULT, "BEID initWithSession return self"); | |
| #endif | |
| return self; | |
| } | |
| - (uint8_t)pinDigit:(uint8_t) digit | |
| { | |
| if ('0' <= digit && digit <= '9') | |
| return digit - '0'; | |
| else if ('A' <= digit && digit <= 'F') | |
| return digit - 'A' + 0x0A; | |
| else if ('a' <= digit && digit <= 'f') | |
| return digit - 'a' + 0x0A; | |
| else { | |
| os_log_error(OS_LOG_DEFAULT, "BEID impossible PIN digit enterred"); | |
| return 0xFF; | |
| } | |
| } | |
| - (BOOL)finishWithError:(NSError * _Nullable __autoreleasing *)error { | |
| #ifdef DEBUG | |
| os_log_error(OS_LOG_DEFAULT, "BEID finishWithError called"); | |
| #endif | |
| //use this if you want to use the auto template set in initWithSession() | |
| /* | |
| if(self.session.authState == BEIDAuthStateFreshlyAuthorized){ | |
| return YES; | |
| } | |
| BOOL (^autoVerify)(NSError**) = ^(NSError** error) { | |
| if(![super finishWithError:error]){ | |
| return NO; | |
| } | |
| self.session.smartCard.context=@(YES); | |
| self.session.authState = BEIDAuthStateFreshlyAuthorized; | |
| return YES; | |
| }; | |
| BOOL success = [self.smartCard inSessionWithError:(NSError **)error executeBlock:(BOOL(^)(NSError **error))autoVerify]; | |
| return success; | |
| */ | |
| BOOL (^verifyPIN)(NSError**) = ^(NSError** error) { | |
| if(self.PIN == nil){ | |
| os_log_error(OS_LOG_DEFAULT, "BEID finishWithError called PIN == nil"); | |
| return NO; | |
| } | |
| uint8_t pin[] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }; | |
| uint64_t PinLength = self.PIN.length; | |
| if(PinLength < 4 || PinLength > 12){ | |
| os_log_error(OS_LOG_DEFAULT, "BEID PIN length not ok, Pinlength = %llu",PinLength); | |
| return NO; | |
| } | |
| [[self.PIN dataUsingEncoding:NSUTF8StringEncoding] getBytes:pin length:PinLength]; | |
| #ifdef DEBUG | |
| os_log(OS_LOG_DEFAULT, "verify PIN with length %lu", (unsigned long)PinLength); | |
| #endif | |
| // Send VERIFY command to the card. | |
| UInt16 sw; | |
| //format PIN | |
| uint8_t apdu[] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }; | |
| uint32_t offset = 0; | |
| apdu[offset++] = 0x20 + PinLength; | |
| for (uint32_t ix = 0; ix < PinLength;ix+=2) | |
| { | |
| apdu[offset++] = ([self pinDigit:pin[ix]] << 4) + ((ix+1) < PinLength ? [self pinDigit:pin[ix+1]] : [self pinDigit:'F']); | |
| } | |
| NSMutableData *PINData = [NSMutableData dataWithBytes:apdu length:8]; | |
| if ([self.smartCard sendIns:0x20 p1:0x00 p2:0x01 data:PINData le:nil sw:&sw error:error] == nil) { | |
| os_log(OS_LOG_DEFAULT, "verify PIN sendIns failed"); | |
| return NO; | |
| } | |
| #ifdef DEBUG | |
| os_log_error(OS_LOG_DEFAULT, "verify PIN returned sw: 0x%04x", sw); | |
| #endif | |
| if ((sw & 0xff00) == 0x6300) { | |
| int triesLeft = sw & 0x3f; | |
| os_log(OS_LOG_DEFAULT, "Failed to verify PIN sw:0x%04x retries: %d", sw, triesLeft); | |
| if (error != nil) { | |
| *error = [NSError errorWithDomain:TKErrorDomain code:TKErrorCodeAuthenticationFailed userInfo: | |
| @{NSLocalizedDescriptionKey: [NSString localizedStringWithFormat: NSLocalizedString(@"VERIFY_TRY_LEFT", nil), triesLeft]}]; | |
| } | |
| return NO; | |
| }else if (sw == 0x6983) { | |
| os_log_error(OS_LOG_DEFAULT, "Failed to verify PIN, PIN blocked sw:0x%04x", sw); | |
| if (error != nil) { | |
| *error = [NSError errorWithDomain:TKErrorDomain code:TKErrorCodeAuthenticationFailed userInfo: | |
| @{NSLocalizedDescriptionKey: [NSString localizedStringWithFormat: NSLocalizedString(@"VPIN_BLOCKED", nil), 0]}]; | |
| } | |
| return NO; | |
| } | |
| else if (sw != 0x9000) { | |
| os_log_error(OS_LOG_DEFAULT, "Failed to verify PIN sw: 0x%04x", sw); | |
| if (error != nil) { | |
| *error = [NSError errorWithDomain:TKErrorDomain code:TKErrorCodeAuthenticationFailed userInfo: | |
| @{NSLocalizedDescriptionKey: [NSString localizedStringWithFormat: NSLocalizedString(@"VERIFY_TRY_LEFT", nil), 0]}]; | |
| } | |
| return NO; | |
| } | |
| return YES; | |
| }; | |
| BOOL success = [self.smartCard inSessionWithError:(NSError **)error executeBlock:(BOOL(^)(NSError **error))verifyPIN]; | |
| if (success){ | |
| // Mark card session sensitive, because we entered PIN into it and no session should access it in this state. | |
| self.smartCard.sensitive = YES; | |
| // Remember in card context that the card is authenticated. | |
| //self.session.smartCard.context = @(YES); | |
| self.smartCard.context = @(YES); | |
| // Mark BEIDTokenSession as freshly authorized. | |
| self.session.authState = BEIDAuthStateFreshlyAuthorized; | |
| } | |
| return success; | |
| } | |
| @end | |
| @implementation BEIDTokenSession | |
| - (instancetype)initWithToken:(BEIDToken *)token { | |
| return [super initWithToken:token]; | |
| } | |
| - (TKTokenAuthOperation *)tokenSession:(TKTokenSession *)session beginAuthForOperation:(TKTokenOperation)operation constraint:(TKTokenOperationConstraint)constraint error:(NSError * _Nullable __autoreleasing *)error { | |
| #ifdef DEBUG | |
| os_log_error(OS_LOG_DEFAULT, "BEID beginAuthForOperation called"); | |
| #endif | |
| if (![constraint isEqual:BEIDConstraintPIN] && ![constraint isEqual:BEIDConstraintPINAlways]) { | |
| os_log_error(OS_LOG_DEFAULT, "attempt to evaluate unsupported constraint %@", constraint); | |
| if (error != nil) { | |
| *error = [NSError errorWithDomain:TKErrorDomain code:TKErrorCodeBadParameter userInfo:@{NSLocalizedDescriptionKey: NSLocalizedString(@"WRONG_CONSTR", nil)}]; | |
| } | |
| return nil; | |
| } | |
| TKTokenAuthOperation * tokenAuth = [[BEIDAuthOperation alloc] initWithSession:self]; | |
| if(tokenAuth == nil) | |
| { | |
| return [[TKTokenAuthOperation alloc] init]; | |
| } | |
| return tokenAuth; | |
| } | |
| - (BOOL)tokenSession:(TKTokenSession *)session supportsOperation:(TKTokenOperation)operation usingKey:(TKTokenObjectID)keyObjectID algorithm:(TKTokenKeyAlgorithm *)algorithm { | |
| #ifdef DEBUG | |
| os_log_error(OS_LOG_DEFAULT, "BEID supportsOperation called"); | |
| #endif | |
| BEIDTokenKeychainKey *keyItem = (BEIDTokenKeychainKey *)[self.token.keychainContents keyForObjectID:keyObjectID error:nil]; | |
| if (keyItem == nil) { | |
| os_log_error(OS_LOG_DEFAULT, "BEID supportsOperation returning NO, keyItem == nil"); | |
| return NO; | |
| } | |
| //NSString* algo = algorithm.description; | |
| //NSData *bytes = [algo dataUsingEncoding:NSUTF8StringEncoding]; | |
| //os_log_error(OS_LOG_DEFAULT, "BEID supportsOperation called, algoritm to check is %s",[bytes bytes]); | |
| /*if([algorithm isAlgorithm:kSecKeyAlgorithmRSASignatureDigestPKCS1v15SHA256]) | |
| { | |
| os_log_error(OS_LOG_DEFAULT, "BEID supportsOperation called, algoritm to check is kSecKeyAlgorithmRSASignatureDigestPKCS1v15SHA256"); | |
| } | |
| if([algorithm isAlgorithm:kSecKeyAlgorithmRSASignatureRaw]) | |
| { | |
| os_log_error(OS_LOG_DEFAULT, "BEID supportsOperation called, algoritm to check is kSecKeyAlgorithmRSASignatureRaw"); | |
| } | |
| if([algorithm supportsAlgorithm:kSecKeyAlgorithmRSASignatureDigestPKCS1v15Raw]) | |
| { | |
| os_log_error(OS_LOG_DEFAULT, "BEID supportsOperation called, supportsAlgorithm kSecKeyAlgorithmRSASignatureDigestPKCS1v15Raw"); | |
| } | |
| if([algorithm supportsAlgorithm:kSecKeyAlgorithmRSASignatureDigestPKCS1v15SHA1]) | |
| { | |
| os_log_error(OS_LOG_DEFAULT, "BEID supportsOperation called, supportsAlgorithm kSecKeyAlgorithmRSASignatureDigestPKCS1v15SHA1"); | |
| } | |
| if([algorithm isAlgorithm:kSecKeyAlgorithmECDSASignatureRFC4754]) | |
| { | |
| os_log_error(OS_LOG_DEFAULT, "BEID supportsOperation called, algoritm to check is kSecKeyAlgorithmECDSASignatureRFC4754"); | |
| } | |
| if([algorithm supportsAlgorithm:kSecKeyAlgorithmECDSASignatureRFC4754]) | |
| { | |
| os_log_error(OS_LOG_DEFAULT, "BEID supportsOperation called, supportsAlgorithm kSecKeyAlgorithmECDSASignatureRFC4754"); | |
| } | |
| if([algorithm isAlgorithm:kSecKeyAlgorithmECDSASignatureDigestX962]) | |
| { | |
| os_log_error(OS_LOG_DEFAULT, "BEID supportsOperation called, isAlgorithm kSecKeyAlgorithmECDSASignatureDigestX962"); | |
| } | |
| if([algorithm supportsAlgorithm:kSecKeyAlgorithmECDSASignatureDigestX962]) | |
| { | |
| os_log_error(OS_LOG_DEFAULT, "BEID supportsOperation called, supportsAlgorithm kSecKeyAlgorithmECDSASignatureDigestX962"); | |
| } | |
| if([algorithm isAlgorithm:kSecKeyAlgorithmECDSASignatureDigestX962SHA1]) | |
| { | |
| os_log_error(OS_LOG_DEFAULT, "BEID supportsOperation called, algoritm to check is kSecKeyAlgorithmECDSASignatureDigestX962SHA1"); | |
| } | |
| if([algorithm isAlgorithm:kSecKeyAlgorithmECDSASignatureDigestX962SHA224]) | |
| { | |
| os_log_error(OS_LOG_DEFAULT, "BEID supportsOperation called, algoritm to check is kSecKeyAlgorithmECDSASignatureDigestX962SHA224"); | |
| } | |
| if([algorithm isAlgorithm:kSecKeyAlgorithmECDSASignatureDigestX962SHA256]) | |
| { | |
| os_log_error(OS_LOG_DEFAULT, "BEID supportsOperation called, algoritm to check is kSecKeyAlgorithmECDSASignatureDigestX962SHA256"); | |
| } | |
| if([algorithm supportsAlgorithm:kSecKeyAlgorithmECDSASignatureDigestX962SHA384]) | |
| { | |
| os_log_error(OS_LOG_DEFAULT, "BEID supportsOperation called, supportsAlgorithm kSecKeyAlgorithmECDSASignatureDigestX962SHA384"); | |
| } | |
| if([algorithm supportsAlgorithm:kSecKeyAlgorithmECDSASignatureDigestX962SHA512]) | |
| { | |
| os_log_error(OS_LOG_DEFAULT, "BEID supportsOperation called, supportsAlgorithm kSecKeyAlgorithmECDSASignatureDigestX962SHA512"); | |
| } | |
| if([algorithm supportsAlgorithm:kSecKeyAlgorithmECDSASignatureMessageX962SHA1]) | |
| { | |
| os_log_error(OS_LOG_DEFAULT, "BEID supportsOperation called, supportsAlgorithm kSecKeyAlgorithmECDSASignatureMessageX962SHA1"); | |
| } | |
| if([algorithm isAlgorithm:kSecKeyAlgorithmECDSASignatureMessageX962SHA224]) | |
| { | |
| os_log_error(OS_LOG_DEFAULT, "BEID supportsOperation called, algoritm to check is kSecKeyAlgorithmECDSASignatureMessageX962SHA224"); | |
| } | |
| if([algorithm supportsAlgorithm:kSecKeyAlgorithmECDSASignatureMessageX962SHA256]) | |
| { | |
| os_log_error(OS_LOG_DEFAULT, "BEID supportsOperation called, supportsAlgorithm kSecKeyAlgorithmECDSASignatureMessageX962SHA256"); | |
| } | |
| if([algorithm supportsAlgorithm:kSecKeyAlgorithmECDSASignatureMessageX962SHA384]) | |
| { | |
| os_log_error(OS_LOG_DEFAULT, "BEID supportsOperation called, supportsAlgorithm kSecKeyAlgorithmECDSASignatureMessageX962SHA384"); | |
| } | |
| if([algorithm supportsAlgorithm:kSecKeyAlgorithmECDSASignatureMessageX962SHA512]) | |
| { | |
| os_log_error(OS_LOG_DEFAULT, "BEID supportsOperation called, supportsAlgorithm kSecKeyAlgorithmECDSASignatureMessageX962SHA512"); | |
| }*/ | |
| switch (operation) { | |
| case TKTokenOperationSignData: | |
| #ifdef DEBUG | |
| os_log_error(OS_LOG_DEFAULT, "BEID supportsOperation called TKTokenOperationSignData"); | |
| #endif | |
| if (keyItem.canSign) { | |
| if ([keyItem.keyType isEqual:(id)kSecAttrKeyTypeRSA]) { | |
| // We support only RAW data format and PKCS1 padding. Once SecKey gets support for PSS padding, | |
| // we should add it here. | |
| BOOL returnValue = ([algorithm isAlgorithm:kSecKeyAlgorithmRSASignatureRaw] && [algorithm supportsAlgorithm:kSecKeyAlgorithmRSASignatureDigestPKCS1v15Raw]); | |
| #ifdef DEBUG | |
| os_log_error(OS_LOG_DEFAULT, "BEID supportsOperation RSA returning %i",returnValue); | |
| os_log_error(OS_LOG_DEFAULT, "BEID supportsOperation RSA description %s",algorithm.description.UTF8String); | |
| #endif | |
| return returnValue; | |
| } | |
| if ([keyItem.keyType isEqual:(id)kSecAttrKeyTypeECDSA]) { | |
| BOOL returnValue = ([algorithm isAlgorithm:kSecKeyAlgorithmECDSASignatureRFC4754] || | |
| [algorithm isAlgorithm:kSecKeyAlgorithmECDSASignatureDigestX962] || | |
| [algorithm isAlgorithm:kSecKeyAlgorithmECDSASignatureDigestX962SHA256] || | |
| [algorithm isAlgorithm:kSecKeyAlgorithmECDSASignatureDigestX962SHA384] || | |
| [algorithm isAlgorithm:kSecKeyAlgorithmECDSASignatureDigestX962SHA512]); | |
| #ifdef DEBUG | |
| os_log_error(OS_LOG_DEFAULT, "BEID supportsOperation ECDSA class name %s",algorithm.className.UTF8String); | |
| os_log_error(OS_LOG_DEFAULT, "BEID supportsOperation ECDSA description %s",algorithm.description.UTF8String); | |
| os_log_error(OS_LOG_DEFAULT, "BEID supportsOperation ECDSA returning %i",returnValue); | |
| #endif | |
| return returnValue; | |
| } | |
| } | |
| break; | |
| default: | |
| os_log_error(OS_LOG_DEFAULT, "BEID supportsOperation called default"); | |
| break; | |
| } | |
| os_log_error(OS_LOG_DEFAULT, "BEID supportsOperation returning NO"); | |
| return NO; | |
| } | |
| - (BEIDTokenKeychainKey *)authenticatedKeyForObjectID:(TKTokenObjectID)keyObjectID error:(NSError **)error { | |
| // Check for authentication status. | |
| #ifdef DEBUG | |
| os_log_error(OS_LOG_DEFAULT, "BEID authenticatedKeyForObjectID called"); | |
| #endif | |
| BEIDTokenKeychainKey *keyItem = (BEIDTokenKeychainKey *)[self.token.keychainContents keyForObjectID:keyObjectID error:error]; | |
| if (keyItem == nil) { | |
| os_log_error(OS_LOG_DEFAULT, "BEID authenticatedKeyForObjectID keyItem == nil"); | |
| return nil; | |
| } | |
| //select key on token | |
| const uint8_t keyId = keyItem.keyID;//0x82 0r 0x83; | |
| uint8_t hashAlgId = 0x01; | |
| #ifdef DEBUG | |
| os_log_error(OS_LOG_DEFAULT, "BEID authenticatedKeyForObjectID keyId = %ux", keyItem.keyID); | |
| #endif | |
| if ([keyItem.keyType isEqual:(id)kSecAttrKeyTypeECDSA] || | |
| [keyItem.keyType isEqual:(id)kSecAttrKeyTypeEC] || | |
| [keyItem.keyType isEqual:(id)kSecAttrKeyTypeECSECPrimeRandom]) | |
| { | |
| hashAlgId = 0x40; | |
| } | |
| BOOL (^selectKey)(NSError**) = ^(NSError** error) { | |
| //first select key and algo | |
| BOOL retVAL = [self selectKeyForSign:keyId algId:hashAlgId smartCard:self.smartCard error:error]; | |
| if (retVAL == NO){ | |
| os_log_error(OS_LOG_DEFAULT, "BEID authenticatedKeyForObjectID selectKeyForSign failed"); | |
| return retVAL; | |
| } | |
| return YES; | |
| }; | |
| if (self.authState == BEIDAuthStateUnauthorized || | |
| (keyItem.alwaysAuthenticate && self.authState == BEIDAuthStateAuthorizedButAlreadyUsed) || | |
| self.smartCard.context == nil) { | |
| BOOL success = [self.smartCard inSessionWithError:(NSError **)error executeBlock:(BOOL(^)(NSError **error))selectKey]; | |
| if(success == NO){ | |
| os_log_error(OS_LOG_DEFAULT, "BEID selectKey failed"); | |
| return nil; | |
| } | |
| #ifdef DEBUG | |
| os_log_error(OS_LOG_DEFAULT, "BEID selectKey success"); | |
| os_log_error(OS_LOG_DEFAULT, "BEID authenticatedKeyForObjectID BEIDAuthStateUnauthorized, setting keyItem = nil"); | |
| #endif | |
| //TODO: move to the (succes == NO section) | |
| if (error != nil) { | |
| *error = [NSError errorWithDomain:TKErrorDomain code:TKErrorCodeAuthenticationNeeded userInfo:nil]; | |
| os_log_error(OS_LOG_DEFAULT, "BEID authenticatedKeyForObjectID failed with error %@",[*error localizedDescription]); | |
| } | |
| return nil; | |
| } | |
| return keyItem; | |
| } | |
| - (BOOL) selectKeyForSign:(const uint8_t)keyId algId:(const uint8_t)algId smartCard:(TKSmartCard *)smartCard error:( NSError **)error | |
| { | |
| #ifdef DEBUG | |
| os_log_error(OS_LOG_DEFAULT, "BEID selectKeyForSign called"); | |
| #endif | |
| // Select signing algorithm, pkcs1 padding and key keyId | |
| //unsigned char command[] = { 0x00, 0x22, 0x41, 0xB6, 0x05, 0x04, 0x80, 0x01, 0x84, keyId }; | |
| unsigned char command[] = { 0x04, 0x80, algId, 0x84, keyId };//40 for unknown hash, EC | |
| //unsigned char command[] = { 0x04, 0x80, 0x08, 0x84, keyId };//8 for sha256 | |
| NSData *data = [NSData dataWithBytes:command length:5]; | |
| UInt16 sw = 0; | |
| [smartCard sendIns:0x22 p1:0x41 p2:0xB6 data:data le:nil sw:&sw error:error]; | |
| if(sw != 0x9000){ | |
| os_log_error(OS_LOG_DEFAULT, "BEID selectKeyForSign failed with SW: %x", sw); | |
| os_log_error(OS_LOG_DEFAULT, "BEID selectKeyForSign failed with error: %@", *error); | |
| if (error != nil) { | |
| *error = [NSError errorWithDomain:TKErrorDomain code:TKErrorCodeObjectNotFound userInfo:nil]; | |
| os_log_error(OS_LOG_DEFAULT, "BEID selectKeyForSign failed with error: %@", *error); | |
| } | |
| return NO; | |
| } | |
| #ifdef DEBUG | |
| os_log_error(OS_LOG_DEFAULT, "BEID selectKeyForSign success"); | |
| #endif | |
| return YES; | |
| } | |
| - (BOOL) signData:(NSData *)data result:(NSData **)result resultLength:(NSNumber *)resultLength smartCard:(TKSmartCard *)smartCard error:(NSError **)error | |
| { | |
| UInt16 sw = 0; | |
| *result = [smartCard sendIns:0x2A p1:0x9E p2:0x9A data:data le:resultLength sw:&sw error:error]; | |
| if(sw != 0x9000){ | |
| os_log_error(OS_LOG_DEFAULT, "BEID signData failed with SW: %x", sw); | |
| os_log_error(OS_LOG_DEFAULT, "BEID signData failed with error: %@", *error); | |
| if (error != nil) { | |
| *error = [NSError errorWithDomain:TKErrorDomain code:TKErrorCodeObjectNotFound userInfo:nil]; | |
| os_log_error(OS_LOG_DEFAULT, "BEID signData failed with error: %@", *error); | |
| } | |
| return NO; | |
| } | |
| #ifdef DEBUG | |
| os_log_error(OS_LOG_DEFAULT, "BEID signData result length is %lu", (unsigned long)(*result).length); | |
| #endif | |
| return YES; | |
| } | |
| // Wrapper around GENERAL AUTHENTICATE card command used for sign. | |
| - (NSData *)generalAuthenticateWithData:(NSData *)data session:(BEIDTokenSession *)session algorithm:(TKTokenKeyAlgorithm *)algorithm usingKey:(TKTokenObjectID)keyObjectID error:(NSError **)error { | |
| #ifdef DEBUG | |
| os_log_error(OS_LOG_DEFAULT, "BEID generalAuthenticateWithData called"); | |
| #endif | |
| BEIDTokenKeychainKey *keyItem = [self authenticatedKeyForObjectID:keyObjectID error:error]; | |
| if (keyItem == nil) { | |
| os_log_error(OS_LOG_DEFAULT, "BEID generalAuthenticateWithData failed, keyItem == nil"); | |
| return nil; | |
| } | |
| NSNumber *resultLen = [NSNumber numberWithUnsignedChar:96]; | |
| //keyItem.keySizeInBits *2 for EC | |
| __block NSData *statResponse; | |
| Byte* dataBytes = (Byte*)data.bytes; | |
| int i = 0; | |
| #ifdef DEBUG | |
| os_log_error(OS_LOG_DEFAULT, "BEID generalAuthenticateWithData original Data to be signed length = %lu, data :",data.length); | |
| for ( i = 0 ; i < data.length ; i++) | |
| { | |
| os_log_error(OS_LOG_DEFAULT, "%d: 0x%x ",i, dataBytes[i]); | |
| } | |
| #endif | |
| if ([algorithm isAlgorithm:kSecKeyAlgorithmRSASignatureRaw] && [algorithm supportsAlgorithm:kSecKeyAlgorithmRSASignatureDigestPKCS1v15Raw]) | |
| { | |
| resultLen = [NSNumber numberWithUnsignedChar:keyItem.keySizeInBits]; | |
| //remove the pkcs1 padding 00 01 ff ff ... ff 00 | |
| for ( i = 0 ; i < data.length ; i++) | |
| { | |
| if ((i > 2)&&(dataBytes[i]!=0xff)) | |
| break; | |
| } | |
| //also remove the ending 0 | |
| i++; | |
| } | |
| NSData * blockData = [[NSData alloc] initWithBytes:&dataBytes[i] length:((data.length)-(i))]; | |
| #ifdef DEBUG | |
| os_log_error(OS_LOG_DEFAULT, "BEID generalAuthenticateWithData tempered Data to be signed length = %lu, data :",blockData.length); | |
| #endif | |
| /* dataBytes = (Byte*)blockData.bytes; | |
| for ( i = 0 ; i < blockData.length ; i++) | |
| { | |
| os_log_error(OS_LOG_DEFAULT, "%d: 0x%x ",i, dataBytes[i]); | |
| }*/ | |
| BOOL (^sign)(NSError**) = ^(NSError** error) { | |
| BOOL retVAL = [self signData:blockData result:&statResponse resultLength:resultLen smartCard:self.smartCard error:error]; | |
| if (retVAL == NO){ | |
| os_log_error(OS_LOG_DEFAULT, "BEID generalAuthenticateWithData signData failed"); | |
| return retVAL; | |
| } | |
| return YES; | |
| }; | |
| BOOL success = [self.smartCard inSessionWithError:(NSError **)error executeBlock:(BOOL(^)(NSError **error))sign]; | |
| if(success == NO){ | |
| os_log_error(OS_LOG_DEFAULT, "BEID sign failed"); | |
| return nil; | |
| } | |
| // Mark BEIDTokenSession as already authorized and authorization used. | |
| session.authState = BEIDAuthStateAuthorizedButAlreadyUsed; | |
| #ifdef DEBUG | |
| os_log_error(OS_LOG_DEFAULT, "BEID signData signature length = %lu",(unsigned long)statResponse.length); | |
| #endif | |
| // Byte* responseBytes = (Byte*)statResponse.bytes; | |
| // int iii; | |
| // for ( iii = 0 ; iii < statResponse.length ; iii++) | |
| // { | |
| // os_log_error(OS_LOG_DEFAULT, "%d: 0x%x ",iii, responseBytes[iii]); | |
| // } | |
| if ([algorithm isAlgorithm:kSecKeyAlgorithmECDSASignatureDigestX962] || | |
| [algorithm isAlgorithm:kSecKeyAlgorithmECDSASignatureDigestX962SHA1] || | |
| [algorithm isAlgorithm:kSecKeyAlgorithmECDSASignatureDigestX962SHA224] || | |
| [algorithm isAlgorithm:kSecKeyAlgorithmECDSASignatureDigestX962SHA256] || | |
| [algorithm isAlgorithm:kSecKeyAlgorithmECDSASignatureDigestX962SHA384] || | |
| [algorithm isAlgorithm:kSecKeyAlgorithmECDSASignatureDigestX962SHA512]) | |
| { | |
| #ifdef DEBUG | |
| os_log_error(OS_LOG_DEFAULT, "BEID package signature into ASN1 sequence"); | |
| #endif | |
| typedef struct { | |
| SecAsn1Item r; | |
| SecAsn1Item s; | |
| } ECDSA; | |
| static const SecAsn1Template ECDSATemplate[] = { | |
| { SEC_ASN1_SEQUENCE, 0, nil, sizeof(ECDSA) }, | |
| { SEC_ASN1_INTEGER, offsetof(ECDSA, r) }, | |
| { SEC_ASN1_INTEGER, offsetof(ECDSA, s) }, | |
| { 0 } //marks the end of the template | |
| }; | |
| uint8 *bytes = (uint8*)statResponse.bytes; | |
| ECDSA ecdsa = { | |
| { statResponse.length / 2, bytes }, | |
| { statResponse.length / 2, bytes + (statResponse.length / 2) }, | |
| }; | |
| SecAsn1CoderRef coder; | |
| SecAsn1CoderCreate(&coder); | |
| SecAsn1Item dest = {0, nil}; | |
| OSStatus ortn = SecAsn1EncodeItem(coder, &ecdsa, ECDSATemplate, &dest); | |
| statResponse = [NSData dataWithBytes:dest.Data length:dest.Length]; | |
| SecAsn1CoderRelease(coder); | |
| //NSLog(@"TokenSession SecAsn1EncodeItem %i %@", ortn, statResponse); | |
| } | |
| return statResponse; | |
| } | |
| - (NSData *)tokenSession:(TKTokenSession *)session signData:(NSData *)dataToSign usingKey:(TKTokenObjectID)keyObjectID algorithm:(TKTokenKeyAlgorithm *)algorithm error:(NSError * _Nullable __autoreleasing *)error { | |
| #ifdef DEBUG | |
| os_log_error(OS_LOG_DEFAULT, "BEID signData called"); | |
| #endif | |
| return [self generalAuthenticateWithData:dataToSign session:(BEIDTokenSession *)session algorithm:(TKTokenKeyAlgorithm *)algorithm usingKey:keyObjectID error:error]; | |
| } | |
| @end |