Skip to content

Commit

Permalink
Implemented support and tests for lock code in ManagementSession.
Browse files Browse the repository at this point in the history
  • Loading branch information
jensutbult committed Jun 5, 2024
1 parent 88240b9 commit 2e802b9
Show file tree
Hide file tree
Showing 7 changed files with 105 additions and 6 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 @@ -18,7 +18,7 @@ @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 @@ -36,6 +36,14 @@ - (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];
}
Expand Down
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 @@ -100,19 +100,26 @@ - (void)readPagedDeviceInfoWithCompletion:(YKFManagementSessionReadPagedDeviceIn
}];
}

- (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,6 +28,7 @@ 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;
Expand Down
28 changes: 27 additions & 1 deletion YubiKitTests/Tests/ManagementTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,9 @@
import XCTest
import Foundation

fileprivate let lockCode = Data(hexEncodedString: "01020304050607080102030405060708")!
fileprivate let clearLockCode = Data(hexEncodedString: "00000000000000000000000000000000")!

class ManagementTests: XCTestCase {
func testDisableOATH() {
runYubiKitTest { connection, completion in
Expand Down Expand Up @@ -116,6 +119,27 @@ stmVersion: \(String(describing: deviceInfo.stmVersion))
}
}

func testLockCode() throws {
runYubiKitTest { connection, completion in
connection.managementSessionAndDeviceInfo { session, deviceInfo in
let config = deviceInfo.configuration!
session.write(config, reboot: false, lockCode: nil, newLockCode: lockCode) { error in
guard error == nil else { XCTFail("Failed setting new lock code"); return }
print("✅ Lock code set to: \(lockCode.hexDescription)")
session.write(config, reboot: false, lockCode: nil) { error in
guard error != nil else { XCTFail("Successfully updated config although no lock code was supplied and it should have been enabled."); return }
print("✅ Failed updating device config (as expected) without using lock code.")
session.write(config, reboot: false, lockCode: lockCode) { error in
guard error == nil else { print("Failed to update device config even though lock code was supplied."); return }
print("✅ Succesfully updated device config using lock code.")
completion()
}
}
}
}
}
}

func testZEnableNFCRestriction() {
runYubiKitTest { connection, completion in
connection.managementSessionAndDeviceInfo { session, deviceInfo in
Expand Down Expand Up @@ -143,7 +167,9 @@ extension YKFConnectionProtocol {
guard let session = session else { XCTAssertTrue(false, "Failed to get Management Session: \(error!)"); return }
session.getDeviceInfo { deviceInfo, error in
guard let deviceInfo = deviceInfo else { XCTAssertTrue(false, "Failed to read device info: \(error!)"); return }
completion(session, deviceInfo)
session.write(deviceInfo.configuration!, reboot: false, lockCode: lockCode, newLockCode: clearLockCode) { error in
completion(session, deviceInfo)
}
}
}
}
Expand Down
12 changes: 12 additions & 0 deletions YubiKitTests/Tests/Utilities/Data+Extensions.swift
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,18 @@
import Foundation

extension Data {

public init?(hexEncodedString: String) {
let string = hexEncodedString.trimmingCharacters(in: .whitespacesAndNewlines).replacingOccurrences(of: " ", with: "")
guard string.count.isMultiple(of: 2) else { return nil }
let chars = string.map { $0 }
let bytes = stride(from: 0, to: chars.count, by: 2)
.map { String(chars[$0]) + String(chars[$0 + 1]) }
.compactMap { UInt8($0, radix: 16) }
guard string.count / bytes.count == 2 else { return nil }
self.init(bytes)
}

var hexDescription: String {
return reduce("") {$0 + String(format: "%02x", $1)}
}
Expand Down

0 comments on commit 2e802b9

Please sign in to comment.