Skip to content

Commit

Permalink
Get assertion with prf extension. WIP.
Browse files Browse the repository at this point in the history
  • Loading branch information
jensutbult committed Nov 6, 2024
1 parent 0b64e79 commit 9fe2fe9
Show file tree
Hide file tree
Showing 7 changed files with 114 additions and 7 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,16 @@ NS_ASSUME_NONNULL_BEGIN
allowList:(NSArray * _Nullable)allowList
pinAuth:(NSData * _Nullable)pinAuth
pinProtocol:(NSUInteger)pinProtocol
options:(NSDictionary * _Nullable)options;

- (nullable instancetype)initWithClientDataHash:(NSData *)clientDataHash
rpId:(NSString *)rpId
allowList:(NSArray * _Nullable)allowList
pinAuth:(NSData * _Nullable)pinAuth
pinProtocol:(NSUInteger)pinProtocol
extensions:(NSDictionary * _Nullable)extensions
options:(NSDictionary * _Nullable)options NS_DESIGNATED_INITIALIZER;

//- (nullable instancetype)initWithRequest:(YKFFIDO2GetAssertionRequest *)request NS_DESIGNATED_INITIALIZER;
- (instancetype)init NS_UNAVAILABLE;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,16 @@ - (nullable instancetype)initWithClientDataHash:(NSData *)clientDataHash
pinAuth:(NSData * _Nullable)pinAuth
pinProtocol:(NSUInteger)pinProtocol
options:(NSDictionary * _Nullable)options {
return [self initWithClientDataHash:clientDataHash rpId:rpId allowList:allowList pinAuth:pinAuth pinProtocol:pinProtocol extensions:nil options:options];
}

- (nullable instancetype)initWithClientDataHash:(NSData *)clientDataHash
rpId:(NSString *)rpId
allowList:(NSArray * _Nullable)allowList
pinAuth:(NSData * _Nullable)pinAuth
pinProtocol:(NSUInteger)pinProtocol
extensions:(NSDictionary * _Nullable)extensions
options:(NSDictionary * _Nullable)options {
YKFAssertAbortInit(clientDataHash);
YKFAssertAbortInit(rpId);

Expand Down Expand Up @@ -68,6 +78,11 @@ - (nullable instancetype)initWithClientDataHash:(NSData *)clientDataHash
requestDictionary[YKFCBORInteger(YKFFIDO2GetAssertionAPDUKeyOptions)] = YKFCBORMap(mutableOptions);
}

// Extensions
if (extensions.count > 0) {
requestDictionary[YKFCBORInteger(YKFFIDO2GetAssertionAPDUKeyExtensions)] = YKFCBORMap(extensions);
}

// Pin Auth
if (pinAuth) {
requestDictionary[YKFCBORInteger(YKFFIDO2GetAssertionAPDUKeyPinAuth)] = YKFCBORByteString(pinAuth);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -453,6 +453,13 @@ typedef NS_ENUM(NSUInteger, YKFFIDO2SessionKeyState) {
options:(NSDictionary * _Nullable)options
completion:(YKFFIDO2SessionGetAssertionCompletionBlock)completion;

- (void)getAssertionWithClientDataHash:(NSData *)clientDataHash
rpId:(NSString *)rpId
allowList:(NSArray * _Nullable)allowList
options:(NSDictionary * _Nullable)options
extensions:(NSDictionary * _Nullable)extensions
completion:(YKFFIDO2SessionGetAssertionCompletionBlock)completion;

/*!
@method getNextAssertionWithCompletion:completion:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -427,6 +427,15 @@ - (void)getAssertionWithClientDataHash:(NSData *)clientDataHash
allowList:(NSArray * _Nullable)allowList
options:(NSDictionary * _Nullable)options
completion:(YKFFIDO2SessionGetAssertionCompletionBlock)completion {
[self getAssertionWithClientDataHash:clientDataHash rpId:rpId allowList:allowList options:options extensions:nil completion:completion];
}

- (void)getAssertionWithClientDataHash:(NSData *)clientDataHash
rpId:(NSString *)rpId
allowList:(NSArray * _Nullable)allowList
options:(NSDictionary * _Nullable)options
extensions:(NSDictionary * _Nullable)extensions
completion:(YKFFIDO2SessionGetAssertionCompletionBlock)completion {
YKFParameterAssertReturn(clientDataHash);
YKFParameterAssertReturn(rpId);
YKFParameterAssertReturn(completion);
Expand All @@ -444,6 +453,58 @@ - (void)getAssertionWithClientDataHash:(NSData *)clientDataHash
}
}

// Extensions, client authenticator input
if (extensions && extensions[@"prf"] && extensions[@"prf"][@"eval"]) {
NSString *base64EncodedFirst = extensions[@"prf"][@"eval"][@"first"];
NSString *base64EncodedSecond = extensions[@"prf"][@"eval"][@"second"];

NSData *first = [[[NSData alloc] initWithBase64EncodedString:base64EncodedFirst options:0] ykf_prfSaltData];
NSData *second = [[[NSData alloc] initWithBase64EncodedString:base64EncodedSecond options:0] ykf_prfSaltData];

if (first.length != 32 || (second && second.length != 32)) {
[NSException raise:@"Invalid input" format:@"Salt is not 32 bytes long."];
}
[self executeGetSharedSecretWithCompletion:^(NSData * _Nullable sharedSecret, YKFCBORMap * _Nullable cosePlatformPublicKey, NSError * _Nullable error) {
NSMutableData *salts = [NSMutableData new];
[salts appendData:first];
if (second) {
[salts appendData:second];
}

NSData *saltEnc = [salts ykf_aes256EncryptedDataWithKey:sharedSecret];
NSData *saltAuth = [saltEnc ykf_fido2HMACWithKey:sharedSecret];
NSMutableDictionary *hmacSecretInput = [NSMutableDictionary new];
hmacSecretInput[YKFCBORInteger(1)] = cosePlatformPublicKey;
hmacSecretInput[YKFCBORInteger(2)] = YKFCBORByteString(saltEnc);
hmacSecretInput[YKFCBORInteger(3)] = YKFCBORByteString([saltAuth subdataWithRange:NSMakeRange(0, 16)]);
hmacSecretInput[YKFCBORInteger(4)] = YKFCBORInteger(1); // pin uv auth protocol version
NSMutableDictionary *authenticatorInputs = [NSMutableDictionary new];
authenticatorInputs[YKFCBORTextString(@"hmac-secret")] = YKFCBORMap(hmacSecretInput);

YKFFIDO2GetAssertionAPDU *apdu = [[YKFFIDO2GetAssertionAPDU alloc] initWithClientDataHash:clientDataHash
rpId:rpId
allowList:allowList
pinAuth:pinAuth
pinProtocol:pinProtocol
extensions:authenticatorInputs
options:options];
ykf_weak_self();
[self executeFIDO2Command:apdu retryCount:0 completion:^(NSData * _Nullable data, NSError * _Nullable error) {
ykf_safe_strong_self();
NSLog(@"%@", data.ykf_hexadecimalString);
NSData *cborData = [strongSelf cborFromKeyResponseData:data];
YKFFIDO2GetAssertionResponse *getAssertionResponse = [[YKFFIDO2GetAssertionResponse alloc] initWithCBORData:cborData];

if (getAssertionResponse) {
completion(getAssertionResponse, nil);
} else {
completion(nil, [YKFFIDO2Error errorWithCode:YKFFIDO2ErrorCodeINVALID_CBOR]);
}
}];
}];
return;
}

YKFFIDO2GetAssertionAPDU *apdu = [[YKFFIDO2GetAssertionAPDU alloc] initWithClientDataHash:clientDataHash
rpId:rpId
allowList:allowList
Expand Down Expand Up @@ -676,4 +737,6 @@ - (void)handleTouchRequired:(YKFAPDU *)apdu retryCount:(int)retryCount completi
});
}



@end
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,8 @@ NS_ASSUME_NONNULL_BEGIN

- (nullable NSData *)ykf_fido2PaddedPinData;

- (NSData *)ykf_prfSaltData;

@end

@interface NSData (NSDATA_PIVAdditions)
Expand Down
9 changes: 9 additions & 0 deletions YubiKit/YubiKit/Helpers/Additions/YKFNSDataAdditions.m
Original file line number Diff line number Diff line change
Expand Up @@ -176,6 +176,15 @@ - (NSData *)ykf_fido2PaddedPinData {
return [mutableData copy];
}

- (NSData *)ykf_prfSaltData {
NSMutableData *mutableData = [NSMutableData new];
[mutableData appendData:[@"WebAuthn PRF" dataUsingEncoding: NSUTF8StringEncoding]];
UInt8 padding = 0x00;
[mutableData appendBytes:&padding length:1];
[mutableData appendData:self];
return [mutableData ykf_SHA256];
}

@end

#pragma mark - PIV
Expand Down
16 changes: 9 additions & 7 deletions YubiKitTests/Tests/FIDO2Tests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -251,11 +251,13 @@ class FIDO2Tests: XCTestCase {
connection.fido2TestSession { session in
session.verifyPin("123456") { error in
if let error { XCTFail("verifyPin failed with: \(error)"); return }
let extensions = ["prf" : true]
session.addCredentialAndAssert(algorithm: YKFFIDO2PublicKeyAlgorithmEdDSA, options: [YKFFIDO2OptionRK: true], extensions: extensions) { response in
let createExtensions = ["prf" : []]
session.addCredentialAndAssert(algorithm: YKFFIDO2PublicKeyAlgorithmES256, options: [YKFFIDO2OptionRK: true], extensions: createExtensions) { response in
print(response.authenticatorData)
print("✅ Created new FIDO2 credential: \(response)")
session.getAssertionAndAssert(response: response, options: [YKFFIDO2OptionUP: true]) { response in

let assertExtensions = ["prf" : ["eval" : ["first" : "abba", "second" : "bebe"]]]
session.getAssertionAndAssert(response: response, options: [YKFFIDO2OptionUP: true], extensions: assertExtensions) { response in
print("✅ Asserted FIDO2 credential: \(response)")
completion()
}
Expand Down Expand Up @@ -335,22 +337,22 @@ extension YKFFIDO2Session {
makeCredential(withClientDataHash: data, rp: rp, user: user, pubKeyCredParams: pubKeyCredParams, excludeList: nil, options: options, extensions: extensions, completion: completion)
}

func getAssertionAndAssert(response: YKFFIDO2MakeCredentialResponse, options: [String: Any]? = nil, completion: @escaping (_ response: YKFFIDO2GetAssertionResponse) -> Void) {
getAssertion(response: response, options: options) { response, error in
func getAssertionAndAssert(response: YKFFIDO2MakeCredentialResponse, options: [String: Any]? = nil, extensions: [String: Any]? = nil, completion: @escaping (_ response: YKFFIDO2GetAssertionResponse) -> Void) {
getAssertion(response: response, options: options, extensions: extensions) { response, error in
guard let response = response else { XCTAssertTrue(false, "🔴 Failed asserting FIDO2 credential: \(error!)"); return }
completion(response)
}
}

func getAssertion(response: YKFFIDO2MakeCredentialResponse, options: [String: Any]? = nil, completion: @escaping YKFFIDO2SessionGetAssertionCompletionBlock) {
func getAssertion(response: YKFFIDO2MakeCredentialResponse, options: [String: Any]? = nil, extensions: [String: Any]? = nil, completion: @escaping YKFFIDO2SessionGetAssertionCompletionBlock) {
let data = Data(repeating: 0, count: 32)
let credentialDescriptor = YKFFIDO2PublicKeyCredentialDescriptor()
credentialDescriptor.credentialId = response.authenticatorData!.credentialId!
let credType = YKFFIDO2PublicKeyCredentialType()
credType.name = "public-key"
credentialDescriptor.credentialType = credType
let allowList = [credentialDescriptor]
getAssertionWithClientDataHash(data, rpId: "yubikit-test.com", allowList: allowList, options: options, completion: completion)
getAssertionWithClientDataHash(data, rpId: "yubikit-test.com", allowList: allowList, options: options, extensions: extensions, completion: completion)
}
}

Expand Down

0 comments on commit 9fe2fe9

Please sign in to comment.