From 88240b92865713a5414ba70a45dde19b5587690c Mon Sep 17 00:00:00 2001 From: Jens Utbult Date: Tue, 4 Jun 2024 13:31:43 +0200 Subject: [PATCH] Restructured device info and config. Implemented nfc restricted, challenge response timeout and auto eject timeout. --- .../Shared/APDU/MGMT/YKFManagementWriteAPDU.m | 13 +++++++ ...ManagementInterfaceConfiguration+Private.h | 10 +---- .../YKFManagementInterfaceConfiguration.h | 8 ++++ .../YKFManagementInterfaceConfiguration.m | 37 +++++++++++------- .../Sessions/MGMT/YKFManagementSession.m | 2 +- .../YKFManagementDeviceInfo+Private.h | 8 +--- .../Shared/Sessions/YKFManagementDeviceInfo.m | 15 +++----- .../Additions/YKFNSMutableDataAdditions.h | 10 +++++ .../Additions/YKFNSMutableDataAdditions.m | 17 +++++++++ YubiKitTests/Tests/ManagementTests.swift | 38 +++++++++++++++++-- 10 files changed, 115 insertions(+), 43 deletions(-) diff --git a/YubiKit/YubiKit/Connections/Shared/APDU/MGMT/YKFManagementWriteAPDU.m b/YubiKit/YubiKit/Connections/Shared/APDU/MGMT/YKFManagementWriteAPDU.m index b588780f..0ffd8350 100644 --- a/YubiKit/YubiKit/Connections/Shared/APDU/MGMT/YKFManagementWriteAPDU.m +++ b/YubiKit/YubiKit/Connections/Shared/APDU/MGMT/YKFManagementWriteAPDU.m @@ -12,6 +12,7 @@ #import "YKFManagementDeviceInfo+Private.h" #import "YKFNSMutableDataAdditions.h" #import "YKFAssert.h" +#import "YKFNSDataAdditions+Private.h" @implementation YKFManagementWriteAPDU @@ -35,6 +36,18 @@ - (instancetype)initWithConfiguration:(nonnull YKFManagementInterfaceConfigurati [configData ykf_appendByte:0]; } + if (configuration.autoEjectTimeout != 0) { + [configData ykf_appendUInt16EntryWithTag:YKFManagementTagAutoEjectTimeout value:configuration.autoEjectTimeout]; + } + + if (configuration.challengeResponseTimeout != 0) { + [configData ykf_appendUInt8EntryWithTag:YKFManagementTagChallengeResponseTimeout value:configuration.challengeResponseTimeout]; + } + + if (configuration.isNFCRestricted) { + [configData ykf_appendShortWithTag:YKFManagementTagNFCRestricted data:0x01]; + } + NSMutableData *rawRequest = [[NSMutableData alloc] init]; [rawRequest ykf_appendByte:configData.length]; [rawRequest appendData:configData]; diff --git a/YubiKit/YubiKit/Connections/Shared/Sessions/MGMT/YKFManagementInterfaceConfiguration+Private.h b/YubiKit/YubiKit/Connections/Shared/Sessions/MGMT/YKFManagementInterfaceConfiguration+Private.h index 1f66b936..4c24071e 100644 --- a/YubiKit/YubiKit/Connections/Shared/Sessions/MGMT/YKFManagementInterfaceConfiguration+Private.h +++ b/YubiKit/YubiKit/Connections/Shared/Sessions/MGMT/YKFManagementInterfaceConfiguration+Private.h @@ -16,20 +16,14 @@ #import "YKFManagementInterfaceConfiguration.h" -@class YKFManagementDeviceInfo; +@class YKFManagementDeviceInfo, YKFTLVRecord; @interface YKFManagementInterfaceConfiguration() -@property (nonatomic, readonly) NSUInteger usbSupportedMask; -@property (nonatomic, readonly) NSUInteger nfcSupportedMask; - -@property (nonatomic, readonly) NSUInteger usbEnabledMask; -@property (nonatomic, readonly) NSUInteger nfcEnabledMask; - @property (nonatomic, readonly) BOOL usbMaskChanged; @property (nonatomic, readonly) BOOL nfcMaskChanged; -- (nullable instancetype)initWithDeviceInfo:(nonnull YKFManagementDeviceInfo *)response NS_DESIGNATED_INITIALIZER; +- (nullable instancetype)initWithTLVRecords:(nonnull NSMutableArray *)records NS_DESIGNATED_INITIALIZER; + (NSUInteger)translateFipsMask:(NSUInteger)mask; diff --git a/YubiKit/YubiKit/Connections/Shared/Sessions/MGMT/YKFManagementInterfaceConfiguration.h b/YubiKit/YubiKit/Connections/Shared/Sessions/MGMT/YKFManagementInterfaceConfiguration.h index cf13e2ad..e5445d9c 100644 --- a/YubiKit/YubiKit/Connections/Shared/Sessions/MGMT/YKFManagementInterfaceConfiguration.h +++ b/YubiKit/YubiKit/Connections/Shared/Sessions/MGMT/YKFManagementInterfaceConfiguration.h @@ -33,6 +33,14 @@ typedef NS_ENUM(NSUInteger, YKFManagementTransportType) { @interface YKFManagementInterfaceConfiguration : NSObject @property (nonatomic, readonly) BOOL isConfigurationLocked; +@property (nonatomic, readwrite) NSTimeInterval autoEjectTimeout; +@property (nonatomic, readwrite) NSTimeInterval challengeResponseTimeout; +@property (nonatomic, readwrite) BOOL isNFCRestricted; + +@property (nonatomic, readonly) NSUInteger usbSupportedMask; +@property (nonatomic, readonly) NSUInteger nfcSupportedMask; +@property (nonatomic, readwrite) NSUInteger usbEnabledMask; +@property (nonatomic, readwrite) NSUInteger nfcEnabledMask; - (BOOL)isEnabled:(YKFManagementApplicationType)application overTransport:(YKFManagementTransportType)transport; - (BOOL)isSupported:(YKFManagementApplicationType)application overTransport:(YKFManagementTransportType)transport; diff --git a/YubiKit/YubiKit/Connections/Shared/Sessions/MGMT/YKFManagementInterfaceConfiguration.m b/YubiKit/YubiKit/Connections/Shared/Sessions/MGMT/YKFManagementInterfaceConfiguration.m index 447327bd..6e0d1ef5 100644 --- a/YubiKit/YubiKit/Connections/Shared/Sessions/MGMT/YKFManagementInterfaceConfiguration.m +++ b/YubiKit/YubiKit/Connections/Shared/Sessions/MGMT/YKFManagementInterfaceConfiguration.m @@ -15,17 +15,15 @@ #import "YKFManagementDeviceInfo+Private.h" #import "YKFManagementDeviceInfo.h" #import "YKFAssert.h" +#import "YKFTLVRecord.h" +#import "NSArray+YKFTLVRecord.h" +#import "YKFNSDataAdditions+Private.h" @interface YKFManagementInterfaceConfiguration() @property (nonatomic, readwrite) BOOL isConfigurationLocked; - @property (nonatomic, readwrite) NSUInteger usbSupportedMask; @property (nonatomic, readwrite) NSUInteger nfcSupportedMask; - -@property (nonatomic, readwrite) NSUInteger usbEnabledMask; -@property (nonatomic, readwrite) NSUInteger nfcEnabledMask; - @property (nonatomic, readwrite) BOOL usbMaskChanged; @property (nonatomic, readwrite) BOOL nfcMaskChanged; @@ -33,16 +31,29 @@ @interface YKFManagementInterfaceConfiguration() @implementation YKFManagementInterfaceConfiguration -- (nullable instancetype)initWithDeviceInfo:(nonnull YKFManagementDeviceInfo *)deviceInfo { - YKFAssertAbortInit(deviceInfo); +- (nullable instancetype)initWithTLVRecords:(nonnull NSMutableArray *)records { self = [super init]; if (self) { - - self.isConfigurationLocked = deviceInfo.isConfigurationLocked; - self.usbSupportedMask = deviceInfo.usbSupportedMask; - self.nfcSupportedMask = deviceInfo.nfcSupportedMask; - self.usbEnabledMask = deviceInfo.usbEnabledMask; - self.nfcEnabledMask = deviceInfo.nfcEnabledMask; + self.isConfigurationLocked = [[records ykfTLVRecordWithTag:YKFManagementTagConfigLocked].value ykf_integerValue] == 1; + self.usbSupportedMask = [[records ykfTLVRecordWithTag:YKFManagementTagUSBSupported].value ykf_integerValue]; + self.usbEnabledMask = [[records ykfTLVRecordWithTag:YKFManagementTagUSBEnabled].value ykf_integerValue]; + self.nfcSupportedMask = [[records ykfTLVRecordWithTag:YKFManagementTagNFCSupported].value ykf_integerValue]; + self.nfcEnabledMask = [[records ykfTLVRecordWithTag:YKFManagementTagNFCEnabled].value ykf_integerValue]; + + NSData *autoEjectTimeoutData = [records ykfTLVRecordWithTag:YKFManagementTagAutoEjectTimeout].value; + if (autoEjectTimeoutData) { + self.autoEjectTimeout = [autoEjectTimeoutData ykf_integerValue]; + } + + NSData *challengeResponseTimeoutData = [records ykfTLVRecordWithTag:YKFManagementTagChallengeResponseTimeout].value; + if (challengeResponseTimeoutData) { + self.challengeResponseTimeout = [challengeResponseTimeoutData ykf_integerValue]; + } + + NSData *isNFCRestrictedData = [records ykfTLVRecordWithTag:YKFManagementTagNFCRestricted].value; + if (isNFCRestrictedData) { + self.isNFCRestricted = [isNFCRestrictedData ykf_integerValue] == 1; + } } return self; } diff --git a/YubiKit/YubiKit/Connections/Shared/Sessions/MGMT/YKFManagementSession.m b/YubiKit/YubiKit/Connections/Shared/Sessions/MGMT/YKFManagementSession.m index b609c727..e579ff3d 100644 --- a/YubiKit/YubiKit/Connections/Shared/Sessions/MGMT/YKFManagementSession.m +++ b/YubiKit/YubiKit/Connections/Shared/Sessions/MGMT/YKFManagementSession.m @@ -66,7 +66,7 @@ - (void)getDeviceInfoWithCompletion:(YKFManagementSessionGetDeviceInfoBlock)comp completion(nil, error); return; } - YKFManagementDeviceInfo *deviceInfo = [[YKFManagementDeviceInfo alloc] initWithResponseData:result defaultVersion:self.version]; + YKFManagementDeviceInfo *deviceInfo = [[YKFManagementDeviceInfo alloc] initWithTLVRecords:result defaultVersion:self.version]; completion(deviceInfo, error); return; } result: [NSMutableArray new] page: 0]; diff --git a/YubiKit/YubiKit/Connections/Shared/Sessions/YKFManagementDeviceInfo+Private.h b/YubiKit/YubiKit/Connections/Shared/Sessions/YKFManagementDeviceInfo+Private.h index 6d223136..53312313 100644 --- a/YubiKit/YubiKit/Connections/Shared/Sessions/YKFManagementDeviceInfo+Private.h +++ b/YubiKit/YubiKit/Connections/Shared/Sessions/YKFManagementDeviceInfo+Private.h @@ -42,13 +42,7 @@ NS_ASSUME_NONNULL_BEGIN @class YKFTLVRecord; @interface YKFManagementDeviceInfo() -@property (nonatomic, readwrite) NSUInteger usbSupportedMask; -@property (nonatomic, readwrite) NSUInteger nfcSupportedMask; - -@property (nonatomic, readwrite) NSUInteger usbEnabledMask; -@property (nonatomic, readwrite) NSUInteger nfcEnabledMask; - -- (nullable instancetype)initWithResponseData:(NSMutableArray *)records defaultVersion:(YKFVersion *)defaultVersion NS_DESIGNATED_INITIALIZER; +- (nullable instancetype)initWithTLVRecords:(NSMutableArray *)records defaultVersion:(YKFVersion *)defaultVersion NS_DESIGNATED_INITIALIZER; - (instancetype)init NS_UNAVAILABLE; diff --git a/YubiKit/YubiKit/Connections/Shared/Sessions/YKFManagementDeviceInfo.m b/YubiKit/YubiKit/Connections/Shared/Sessions/YKFManagementDeviceInfo.m index 03893afa..2c27b70a 100644 --- a/YubiKit/YubiKit/Connections/Shared/Sessions/YKFManagementDeviceInfo.m +++ b/YubiKit/YubiKit/Connections/Shared/Sessions/YKFManagementDeviceInfo.m @@ -43,7 +43,7 @@ @interface YKFManagementDeviceInfo() @implementation YKFManagementDeviceInfo -- (nullable instancetype)initWithResponseData:(nonnull NSMutableArray *)records defaultVersion:(nonnull YKFVersion *)defaultVersion { +- (nullable instancetype)initWithTLVRecords:(nonnull NSMutableArray *)records defaultVersion:(nonnull YKFVersion *)defaultVersion { YKFAssertAbortInit(records.count > 0); YKFAssertAbortInit(defaultVersion) self = [super init]; @@ -98,28 +98,23 @@ - (nullable instancetype)initWithResponseData:(nonnull NSMutableArray 0); + [self ykf_appendByte:tag]; + [self ykf_appendByte:sizeof(UInt8)]; + [self appendBytes:&value length:sizeof(UInt8)]; +} + +- (void)ykf_appendUInt16EntryWithTag:(UInt8)tag value:(UInt16)value { + YKFParameterAssertReturn(tag > 0); + + UInt16 bigEndianValue = CFSwapInt16HostToBig(value); + + [self ykf_appendByte:tag]; + [self ykf_appendByte:sizeof(UInt16)]; + [self appendBytes:&bigEndianValue length:sizeof(UInt16)]; +} + - (void)ykf_appendUInt32EntryWithTag:(UInt8)tag value:(UInt32)value { YKFParameterAssertReturn(tag > 0); diff --git a/YubiKitTests/Tests/ManagementTests.swift b/YubiKitTests/Tests/ManagementTests.swift index e26589e5..d0648921 100644 --- a/YubiKitTests/Tests/ManagementTests.swift +++ b/YubiKitTests/Tests/ManagementTests.swift @@ -95,15 +95,45 @@ class ManagementTests: XCTestCase { session.getDeviceInfo { deviceInfo, error in guard let deviceInfo = deviceInfo else { XCTFail("Failed to get DeviceInfo: \(error!)"); return } print("✅ Got device info:") - print(" is locked: \(deviceInfo.isConfigurationLocked)") - print(" serial number: \(deviceInfo.serialNumber)") - print(" form factor: \(deviceInfo.formFactor.rawValue)") - print(" firmware version: \(deviceInfo.version)") + print(""" +YubiKey \(deviceInfo.formFactor) \(deviceInfo.version) (#\(deviceInfo.serialNumber)) +Supported capabilities: \(String(describing: deviceInfo.configuration?.nfcSupportedMask)) +Supported capabilities: \(String(describing: deviceInfo.configuration?.usbSupportedMask)) +isConfigLocked: \(deviceInfo.isConfigurationLocked) +isFips: \(deviceInfo.isFips) +isSky: \(deviceInfo.isSky) +partNumber: \(String(describing: deviceInfo.partNumber)) +isFipsCapable: \(deviceInfo.isFIPSCapable) +isFipsApproved: \(deviceInfo.isFIPSApproved) +pinComplexity: \(deviceInfo.pinComplexity) +resetBlocked: \(deviceInfo.isResetBlocked) +fpsVersion: \(String(describing: deviceInfo.fpsVersion)) +stmVersion: \(String(describing: deviceInfo.stmVersion)) +""") completion() } } } } + + func testZEnableNFCRestriction() { + runYubiKitTest { connection, completion in + connection.managementSessionAndDeviceInfo { session, deviceInfo in + guard let config = deviceInfo.configuration else { completion(); return } + config.isNFCRestricted = true + session.write(config, reboot: false) { error in + XCTAssertNil(error) + session.getDeviceInfo { deviceInfo, error in + XCTAssertNil(error) + if let isNFCRestricted = deviceInfo?.configuration?.isNFCRestricted { + XCTAssertTrue(isNFCRestricted) + } + completion() + } + } + } + } + } } extension YKFConnectionProtocol {