Skip to content

Commit

Permalink
Device reset for Bio Multi-protocol keys.
Browse files Browse the repository at this point in the history
  • Loading branch information
jensutbult committed Jun 19, 2024
1 parent 5cbb858 commit 5e52be8
Show file tree
Hide file tree
Showing 5 changed files with 82 additions and 0 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,14 @@ typedef void (^YKFManagementSessionGetDeviceInfoBlock)
/// parameter is nil.
typedef void (^YKFManagementSessionWriteCompletionBlock) (NSError* _Nullable error);

/// @abstract
/// Response block for [deviceReset:completion:] which will do a device reset on a YubiKey Biuo
///
/// @param error
/// In case of a failed request this parameter contains the error. If the request was successful this
/// parameter is nil.
typedef void (^YKFManagementSessionDeviceResetCompletionBlock) (NSError* _Nullable error);

NS_ASSUME_NONNULL_BEGIN

/// @abstract Defines the interface for YKFManagementSessionProtocol.
Expand Down Expand Up @@ -127,6 +135,19 @@ NS_ASSUME_NONNULL_BEGIN
/// The method is thread safe and can be invoked from any thread (main or a background thread).
- (void)writeConfiguration:(YKFManagementInterfaceConfiguration*)configuration reboot:(BOOL)reboot completion:(nonnull YKFManagementSessionWriteCompletionBlock)completion;

/// @abstract
/// Perform a device-wide reset in Bio Multi-protocol Edition devices
///
/// @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 reset, available in YubiKey 5.6 or later.
/// The method is thread safe and can be invoked from any thread (main or a background thread).
- (void)deviceReset:(YKFManagementSessionDeviceResetCompletionBlock)completion;


- (instancetype)init NS_UNAVAILABLE;

@end
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,17 @@ - (void)writeConfiguration:(YKFManagementInterfaceConfiguration*)configuration r
[self writeConfiguration:configuration reboot:reboot lockCode:nil newLockCode:nil completion:completion];
}

- (void)deviceReset:(YKFManagementSessionDeviceResetCompletionBlock)completion {
if (![self.features.deviceReset isSupportedBySession:self]) {
completion([[NSError alloc] initWithDomain:YKFManagementErrorDomain code:YKFManagementErrorCodeUnsupportedOperation userInfo:@{NSLocalizedDescriptionKey: @"Device reset not supported by this YubiKey."}]);
return;
}
YKFAPDU *apdu = [[YKFAPDU alloc] initWithCla:0 ins:0x1f p1:0 p2:0 data:[NSData data] type:YKFAPDUTypeExtended];
[self.smartCardInterface executeCommand:apdu completion:^(NSData * _Nullable data, NSError * _Nullable error) {
completion(error);
}];
}

// 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 @@ -14,6 +14,7 @@
@interface YKFManagementSessionFeatures : NSObject
@property (nonatomic, readonly) YKFFeature * _Nonnull deviceInfo;
@property (nonatomic, readonly) YKFFeature * _Nonnull deviceConfig;
@property (nonatomic, readonly) YKFFeature * _Nonnull deviceReset;
@end

#endif /* YKFManagementSessionFeatures_h */
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
@interface YKFManagementSessionFeatures()
@property (nonatomic, readwrite) YKFFeature * _Nonnull deviceInfo;
@property (nonatomic, readwrite) YKFFeature * _Nonnull deviceConfig;
@property (nonatomic, readwrite) YKFFeature * _Nonnull deviceReset;
@end

@implementation YKFManagementSessionFeatures
Expand All @@ -28,6 +29,7 @@ - (instancetype)init {
if (self) {
self.deviceInfo = [[YKFFeature alloc] initWithName:@"Device info" versionString:@"4.1.0"];
self.deviceConfig = [[YKFFeature alloc] initWithName:@"Device config" versionString:@"5.0.0"];
self.deviceReset = [[YKFFeature alloc] initWithName:@"Device reset" versionString:@"5.6.0"];
}
return self;
}
Expand Down
47 changes: 47 additions & 0 deletions YubiKitTests/Tests/ManagementTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,53 @@ stmVersion: \(String(describing: deviceInfo.stmVersion))
}
}
}

func testBioDeviceReset() throws {
runYubiKitTest { connection, completion in
connection.managementSession { session, error in
guard let session else { XCTFail("Failed to get Management Session: \(error!)"); return }
session.getDeviceInfo { info, error in
guard let info else { XCTFail("Failed to get device info: \(error!)"); return }
guard info.formFactor == .usbaBio || info.formFactor == .usbcBio else {
print("⚠️ Skip testBioDeviceReset()")
completion()
return
}
session.deviceReset { error in
XCTAssertNil(error)
connection.pivSession { session, error in
guard let session else { XCTFail("Failed to get PIV Session: \(error!)"); return }
session.getPinMetadata { isDefault, _, _, error in
XCTAssertNil(error)
XCTAssertTrue(isDefault)
session.setPin("654321", oldPin: "123456") { error in
XCTAssertNil(error)
session.getPinMetadata { isDefault, _, _, error in
XCTAssertNil(error)
XCTAssertFalse(isDefault)
connection.managementSession { session, error in
guard let session else { XCTFail("Failed to get Management Session: \(error!)"); return }
session.deviceReset { error in
XCTAssertNil(error)
connection.pivSession { session, error in
guard let session else { XCTFail("Failed to get PIV Session: \(error!)"); return }
session.getPinMetadata { isDefault, _, _, error in
XCTAssertNil(error)
XCTAssertTrue(isDefault)
completion()
}
}
}
}
}
}
}
}
}
}
}
}
}
}

extension YKFConnectionProtocol {
Expand Down

0 comments on commit 5e52be8

Please sign in to comment.