Skip to content

Commit

Permalink
Merge branch 'jens/management-application-5.7'
Browse files Browse the repository at this point in the history
  • Loading branch information
jensutbult committed Jun 19, 2024
2 parents 2c29a51 + 2e802b9 commit e79e143
Show file tree
Hide file tree
Showing 14 changed files with 366 additions and 70 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ NS_ASSUME_NONNULL_BEGIN

@interface YKFManagementWriteAPDU : YKFAPDU

- (instancetype)initWithConfiguration:(nonnull YKFManagementInterfaceConfiguration*)configuration reboot:(BOOL)reboot NS_DESIGNATED_INITIALIZER;
- (instancetype)initWithConfiguration:(nonnull YKFManagementInterfaceConfiguration*)configuration reboot:(BOOL)reboot lockCode:(nullable NSData *)lockCode newLockCode:(nullable NSData *)newLockCode NS_DESIGNATED_INITIALIZER;
- (instancetype)init NS_UNAVAILABLE;

@end
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,13 @@
#import "YKFManagementDeviceInfo+Private.h"
#import "YKFNSMutableDataAdditions.h"
#import "YKFAssert.h"
#import "YKFNSDataAdditions+Private.h"

@implementation YKFManagementWriteAPDU

static UInt8 const YKFManagementConfigurationTagsReboot = 0x0c;

- (instancetype)initWithConfiguration:(nonnull YKFManagementInterfaceConfiguration*)configuration reboot:(BOOL)reboot {
- (instancetype)initWithConfiguration:(nonnull YKFManagementInterfaceConfiguration*)configuration reboot:(BOOL)reboot lockCode:(NSData *)lockCode newLockCode:(NSData *)newLockCode {
YKFAssertAbortInit(configuration);

NSMutableData *configData = [[NSMutableData alloc] init];
Expand All @@ -35,6 +36,26 @@ - (instancetype)initWithConfiguration:(nonnull YKFManagementInterfaceConfigurati
[configData ykf_appendByte:0];
}

if (lockCode) {
[configData ykf_appendEntryWithTag:YKFManagementTagUnlock data:lockCode];
}

if (newLockCode) {
[configData ykf_appendEntryWithTag:YKFManagementTagConfigLocked data:newLockCode];
}

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];
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,20 +16,16 @@

#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<YKFTLVRecord*> *)records NS_DESIGNATED_INITIALIZER;

+ (NSUInteger)translateFipsMask:(NSUInteger)mask;

@end

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ typedef NS_ENUM(NSUInteger, YKFManagementApplicationType) {
YKFManagementApplicationTypeOPGP = 0x08,
YKFManagementApplicationTypePIV = 0x10,
YKFManagementApplicationTypeOATH = 0x20,
YKFManagementApplicationTypeHSMAUTH = 0x0100,
YKFManagementApplicationTypeCTAP2 = 0x0200
};

Expand All @@ -32,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;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,34 +15,45 @@
#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;

@end

@implementation YKFManagementInterfaceConfiguration

- (nullable instancetype)initWithDeviceInfo:(nonnull YKFManagementDeviceInfo *)deviceInfo {
YKFAssertAbortInit(deviceInfo);
- (nullable instancetype)initWithTLVRecords:(nonnull NSMutableArray<YKFTLVRecord*> *)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;
}
Expand Down Expand Up @@ -98,4 +109,25 @@ - (void) setEnabled: (BOOL)newValue application:(YKFManagementApplicationType)ap
}
}

+ (NSUInteger)translateFipsMask:(NSUInteger)fipsMask {
NSUInteger capabilities = 0;
if ((fipsMask & 0b00000001) != 0) {
capabilities |= YKFManagementApplicationTypeOTP;
}
if ((fipsMask & 0b00000010) != 0) {
capabilities |= YKFManagementApplicationTypePIV;
}
if ((fipsMask & 0b00000100) != 0) {
capabilities |= YKFManagementApplicationTypeOPGP;
}
if ((fipsMask & 0b00001000) != 0) {
capabilities |= YKFManagementApplicationTypeOATH;
}
if ((fipsMask & 0b00010000) != 0) {
capabilities |= YKFManagementApplicationTypeHSMAUTH;
}
return capabilities;
}


@end
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,51 @@ NS_ASSUME_NONNULL_BEGIN
/// The method is thread safe and can be invoked from any thread (main or a background thread).
- (void)getDeviceInfoWithCompletion:(YKFManagementSessionGetDeviceInfoBlock)completion;

/// @abstract
/// Writes configuration to YubiKey (allos to enable and disable applications on YubiKey)
///
/// @param configuration
/// The configurations that represent information on which interfaces/applications need to be enabled
///
/// @param reboot
/// The device reboots after setting configuration.
///
/// @param lockCode
/// Required if a configuration lock code is set.
///
/// @param newLockCode
/// changes or removes (if 16 byte all-zero) the configuration lock code.
///
/// @param completion
/// The response block which is executed after the request was processed by the key. The completion block
/// will be executed on a background thread.
///
/// @note
/// This method requires support for device config, available in YubiKey 5.0 or later.
/// The method is thread safe and can be invoked from any thread (main or a background thread).
- (void)writeConfiguration:(YKFManagementInterfaceConfiguration*)configuration reboot:(BOOL)reboot lockCode:(nullable NSData *)lockCode newLockCode:(nullable NSData *)newLockCode completion:(nonnull YKFManagementSessionWriteCompletionBlock)completion;

/// @abstract
/// Writes configuration to YubiKey (allos to enable and disable applications on YubiKey)
///
/// @param configuration
/// The configurations that represent information on which interfaces/applications need to be enabled
///
/// @param reboot
/// The device reboots after setting configuration.
///
/// @param lockCode
/// Required if a configuration lock code is set.
///
/// @param completion
/// The response block which is executed after the request was processed by the key. The completion block
/// will be executed on a background thread.
///
/// @note
/// This method requires support for device config, available in YubiKey 5.0 or later.
/// The method is thread safe and can be invoked from any thread (main or a background thread).
- (void)writeConfiguration:(YKFManagementInterfaceConfiguration*)configuration reboot:(BOOL)reboot lockCode:(nullable NSData *)lockCode completion:(nonnull YKFManagementSessionWriteCompletionBlock)completion;

/// @abstract
/// Writes configuration to YubiKey (allos to enable and disable applications on YubiKey)
///
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@
#import "YKFSmartCardInterface.h"
#import "YKFSelectApplicationAPDU.h"
#import "YKFFeature.h"
#import "NSArray+YKFTLVRecord.h"
#import "YKFTLVRecord.h"

NSString* const YKFManagementErrorDomain = @"com.yubico.management";

Expand Down Expand Up @@ -59,26 +61,65 @@ - (void)getDeviceInfoWithCompletion:(YKFManagementSessionGetDeviceInfoBlock)comp
completion(nil, [[NSError alloc] initWithDomain:YKFManagementErrorDomain code:YKFManagementErrorCodeUnsupportedOperation userInfo:@{NSLocalizedDescriptionKey: @"Device info not supported by this YubiKey."}]);
return;
}
YKFAPDU *apdu = [[YKFAPDU alloc] initWithCla:0x00 ins:0x1D p1:0x00 p2:0x00 data:[NSData data] type:YKFAPDUTypeShort];
[self.smartCardInterface executeCommand:apdu completion:^(NSData * _Nullable data, NSError * _Nullable error) {
YKFManagementDeviceInfo *deviceInfo = [[YKFManagementDeviceInfo alloc] initWithResponseData:data defaultVersion:self.version];
[self readPagedDeviceInfoWithCompletion:^(NSMutableArray<YKFTLVRecord *> *result, NSError * _Nullable error) {
if (error) {
completion(nil, error);
return;
}
YKFManagementDeviceInfo *deviceInfo = [[YKFManagementDeviceInfo alloc] initWithTLVRecords:result defaultVersion:self.version];
completion(deviceInfo, error);
return;
} result: [NSMutableArray<YKFTLVRecord *> new] page: 0];
}

typedef void (^YKFManagementSessionReadPagedDeviceInfoBlock)
(NSMutableArray<YKFTLVRecord *>* result, NSError* _Nullable error);

- (void)readPagedDeviceInfoWithCompletion:(YKFManagementSessionReadPagedDeviceInfoBlock)completion result:(NSMutableArray<YKFTLVRecord *>* _Nonnull)result page:(UInt8)page {
YKFAPDU *apdu = [[YKFAPDU alloc] initWithCla:0x00 ins:0x1D p1:page p2:0x00 data:[NSData data] type:YKFAPDUTypeShort];
[self.smartCardInterface executeCommand:apdu completion:^(NSData * _Nullable data, NSError * _Nullable error) {
if (error) {
completion(nil, error);
return;
}
const char* bytes = (const char*)[data bytes];
int length = bytes[0] & 0xff;
if (length != data.length - 1) {
completion(result, nil);
return;
}
NSArray<YKFTLVRecord*> *records = [YKFTLVRecord sequenceOfRecordsFromData:[data subdataWithRange:NSMakeRange(1, data.length - 1)]];
[result addObjectsFromArray:records];
if ([records ykfTLVRecordWithTag:0x10] != nil) {
[self readPagedDeviceInfoWithCompletion:completion result:result page:page + 1];
return;
} else {
completion(result, nil);
return;
}
}];
}

- (void)writeConfiguration:(YKFManagementInterfaceConfiguration*)configuration reboot:(BOOL)reboot completion:(nonnull YKFManagementSessionWriteCompletionBlock)completion {
YKFParameterAssertReturn(configuration);
- (void)writeConfiguration:(YKFManagementInterfaceConfiguration*)configuration reboot:(BOOL)reboot lockCode:(nullable NSData *)lockCode newLockCode:(nullable NSData *)newLockCode completion:(nonnull YKFManagementSessionWriteCompletionBlock)completion {
YKFParameterAssertReturn(configuration);
if (![self.features.deviceConfig isSupportedBySession:self]) {
completion([[NSError alloc] initWithDomain:YKFManagementErrorDomain code:YKFManagementErrorCodeUnsupportedOperation userInfo:@{NSLocalizedDescriptionKey: @"Writing device configuration not supported by this YubiKey."}]);
return;
}
YKFManagementWriteAPDU *apdu = [[YKFManagementWriteAPDU alloc]initWithConfiguration:configuration reboot:reboot];
YKFManagementWriteAPDU *apdu = [[YKFManagementWriteAPDU alloc]initWithConfiguration:configuration reboot:reboot lockCode:lockCode newLockCode:newLockCode];
[self.smartCardInterface executeCommand:apdu completion:^(NSData * _Nullable data, NSError * _Nullable error) {
completion(error);
}];
}

- (void)writeConfiguration:(YKFManagementInterfaceConfiguration*)configuration reboot:(BOOL)reboot lockCode:(nullable NSData *)lockCode completion:(nonnull YKFManagementSessionWriteCompletionBlock)completion {
[self writeConfiguration:configuration reboot:reboot lockCode:lockCode newLockCode:nil completion:completion];
}

- (void)writeConfiguration:(YKFManagementInterfaceConfiguration*)configuration reboot:(BOOL)reboot completion:(nonnull YKFManagementSessionWriteCompletionBlock)completion {
[self writeConfiguration:configuration reboot:reboot lockCode:nil newLockCode:nil completion:completion];
}

// No application side state that needs clearing but this will be called when another
// session is replacing the YKFManagementSession.
- (void)clearSessionState {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,18 +28,22 @@ static const NSUInteger YKFManagementTagDeviceFlags = 0x08;
static const NSUInteger YKFManagementTagNFCSupported = 0x0d;
static const NSUInteger YKFManagementTagNFCEnabled = 0x0e;
static const NSUInteger YKFManagementTagConfigLocked = 0x0a;
static const NSUInteger YKFManagementTagUnlock = 0x0b;
static const NSUInteger YKFManagementTagPartNumber = 0x13;
static const NSUInteger YKFManagementTagFIPSCapable = 0x14;
static const NSUInteger YKFManagementTagFIPSApproved = 0x15;
static const NSUInteger YKFManagementTagPINComplexity = 0x16;
static const NSUInteger YKFManagementTagNFCRestricted = 0x17;
static const NSUInteger YKFManagementTagResetBlocked = 0x18;
static const NSUInteger YKFManagementTagFPSVersion = 0x20;
static const NSUInteger YKFManagementTagSTMVersion = 0x21;

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:(NSData *)data defaultVersion:(YKFVersion *)defaultVersion NS_DESIGNATED_INITIALIZER;
- (nullable instancetype)initWithTLVRecords:(NSMutableArray<YKFTLVRecord*> *)records defaultVersion:(YKFVersion *)defaultVersion NS_DESIGNATED_INITIALIZER;

- (instancetype)init NS_UNAVAILABLE;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,10 +23,18 @@ typedef NS_ENUM(NSUInteger, YKFFormFactor) {
YKFFormFactorUnknown = 0x00,
/// A keychain-sized YubiKey with a USB-A connector.
YKFFormFactorUSBAKeychain = 0x01,
/// A nano-sized YubiKey with a USB-A connector.
YKFFormFactorUSBANano = 0x02,
/// A keychain-sized YubiKey with a USB-C connector.
YKFFormFactorUSBCKeychain = 0x03,
/// A nano-sized YubiKey with a USB-C connector.
YKFFormFactorUSBCNano = 0x04,
/// A keychain-sized YubiKey with both USB-C and Lightning connectors.
YKFFormFactorUSBCLightning = 0x05,
/// A keychain-sized YubiKey with fingerprint sensor and USB-A connector.
YKFFormFactorUSBABio = 0x06,
/// A keychain-sized YubiKey with fingerprint sensor and USB-C connector.
YKFFormFactorUSBCBio = 0x07,
};

NS_ASSUME_NONNULL_BEGIN
Expand All @@ -35,10 +43,19 @@ NS_ASSUME_NONNULL_BEGIN

@property (nonatomic, readonly, nullable) YKFManagementInterfaceConfiguration* configuration;

@property (nonatomic, readonly) NSUInteger serialNumber;
@property (nonatomic, readonly) YKFVersion *version;
@property (nonatomic, readonly) YKFFormFactor formFactor;
@property (nonatomic, readonly) NSUInteger serialNumber;
@property (nonatomic, readonly, nullable) NSString* partNumber;
@property (nonatomic, readonly) NSUInteger isFIPSCapable;
@property (nonatomic, readonly) NSUInteger isFIPSApproved;
@property (nonatomic, readonly, nullable) YKFVersion *fpsVersion;
@property (nonatomic, readonly, nullable) YKFVersion *stmVersion;
@property (nonatomic, readonly) bool isConfigurationLocked;
@property (nonatomic, readonly) bool isFips;
@property (nonatomic, readonly) bool isSky;
@property (nonatomic, readonly) bool pinComplexity;
@property (nonatomic, readonly) NSUInteger isResetBlocked;

@end

Expand Down
Loading

0 comments on commit e79e143

Please sign in to comment.