From 9a26bd09a1c23990b771a828006fb36956f950b8 Mon Sep 17 00:00:00 2001 From: Jens Utbult Date: Wed, 6 Nov 2024 17:04:21 +0100 Subject: [PATCH] Get assertion with prf extension output parsing. --- .../YKFFIDO2GetAssertionResponse+Private.h | 3 +- .../FIDO2/YKFFIDO2GetAssertionResponse.h | 2 + .../FIDO2/YKFFIDO2GetAssertionResponse.m | 38 ++++++++++++++++++- .../FIDO2/YKFFIDO2MakeCredentialResponse.h | 4 ++ .../FIDO2/YKFFIDO2MakeCredentialResponse.m | 2 - .../Shared/Sessions/FIDO2/YKFFIDO2Session.m | 2 +- 6 files changed, 45 insertions(+), 6 deletions(-) diff --git a/YubiKit/YubiKit/Connections/Shared/Requests/FIDO2/YKFFIDO2GetAssertionResponse+Private.h b/YubiKit/YubiKit/Connections/Shared/Requests/FIDO2/YKFFIDO2GetAssertionResponse+Private.h index e1a4e947..a4e5a14f 100644 --- a/YubiKit/YubiKit/Connections/Shared/Requests/FIDO2/YKFFIDO2GetAssertionResponse+Private.h +++ b/YubiKit/YubiKit/Connections/Shared/Requests/FIDO2/YKFFIDO2GetAssertionResponse+Private.h @@ -19,7 +19,8 @@ NS_ASSUME_NONNULL_BEGIN @interface YKFFIDO2GetAssertionResponse() -- (nullable instancetype)initWithCBORData:(NSData *)cborData NS_DESIGNATED_INITIALIZER; +- (nullable instancetype)initWithCBORData:(NSData *)cborData sharedSecret:(NSData * _Nullable)sharedSecret NS_DESIGNATED_INITIALIZER; +- (nullable instancetype)initWithCBORData:(NSData *)cborData; @end diff --git a/YubiKit/YubiKit/Connections/Shared/Requests/FIDO2/YKFFIDO2GetAssertionResponse.h b/YubiKit/YubiKit/Connections/Shared/Requests/FIDO2/YKFFIDO2GetAssertionResponse.h index 93b01357..d2025ed5 100644 --- a/YubiKit/YubiKit/Connections/Shared/Requests/FIDO2/YKFFIDO2GetAssertionResponse.h +++ b/YubiKit/YubiKit/Connections/Shared/Requests/FIDO2/YKFFIDO2GetAssertionResponse.h @@ -84,6 +84,8 @@ NS_ASSUME_NONNULL_BEGIN */ @property (nonatomic, readonly) NSData *rawResponse; +@property (nonatomic, readonly, nullable) NSDictionary *extensionsOutput; + /* Not available: the response will be created by the library. */ diff --git a/YubiKit/YubiKit/Connections/Shared/Requests/FIDO2/YKFFIDO2GetAssertionResponse.m b/YubiKit/YubiKit/Connections/Shared/Requests/FIDO2/YKFFIDO2GetAssertionResponse.m index 18c13290..5a27928b 100644 --- a/YubiKit/YubiKit/Connections/Shared/Requests/FIDO2/YKFFIDO2GetAssertionResponse.m +++ b/YubiKit/YubiKit/Connections/Shared/Requests/FIDO2/YKFFIDO2GetAssertionResponse.m @@ -13,6 +13,9 @@ // limitations under the License. #import "YKFFIDO2GetAssertionResponse.h" +#import "YKFFIDO2MakeCredentialResponse.h" +#import "YKFNSDataAdditions+Private.h" +#import "YKFNSDataAdditions.h" #import "YKFFIDO2GetAssertionResponse+Private.h" #import "YKFCBORDecoder.h" #import "YKFFIDO2Type.h" @@ -33,6 +36,7 @@ @interface YKFFIDO2GetAssertionResponse() @property (nonatomic, readwrite) NSData *signature; @property (nonatomic, readwrite) YKFFIDO2PublicKeyCredentialUserEntity *user; @property (nonatomic, readwrite) NSInteger numberOfCredentials; +@property (nonatomic, readwrite) NSDictionary *extensionsOutput; @property (nonatomic, readwrite) NSData *rawResponse; @@ -40,7 +44,12 @@ @interface YKFFIDO2GetAssertionResponse() @implementation YKFFIDO2GetAssertionResponse + - (instancetype)initWithCBORData:(NSData *)cborData { + return [self initWithCBORData:cborData sharedSecret:nil]; +} + +- (instancetype)initWithCBORData:(NSData *)cborData sharedSecret:(NSData * _Nullable)sharedSecret { self = [super init]; if (self) { YKFAssertAbortInit(cborData); @@ -55,7 +64,7 @@ - (instancetype)initWithCBORData:(NSData *)cborData { YKFAssertAbortInit(responseMap); - BOOL success = [self parseResponseMap: responseMap]; + BOOL success = [self parseResponseMap: responseMap sharedSecret:sharedSecret]; YKFAssertAbortInit(success); } return self; @@ -63,7 +72,11 @@ - (instancetype)initWithCBORData:(NSData *)cborData { #pragma mark - Private -- (BOOL)parseResponseMap:(YKFCBORMap *)map { +//- (BOOL)parseResponseMap:(YKFCBORMap *)map { +// return [self parseResponseMap:map sharedSecret:nil]; +//} + +- (BOOL)parseResponseMap:(YKFCBORMap *)map sharedSecret:(NSData *)sharedSecret { id convertedObject = [YKFCBORDecoder convertCBORObjectToFoundationType:map]; if (!convertedObject || ![convertedObject isKindOfClass:NSDictionary.class]) { return NO; @@ -97,6 +110,27 @@ - (BOOL)parseResponseMap:(YKFCBORMap *)map { YKFAssertReturnValue(authData, @"authenticatorGetAssertion authData is required.", NO); self.authData = authData; + // Extensions output + YKFFIDO2AuthenticatorData *authenticatorData = [[YKFFIDO2AuthenticatorData alloc] initWithData: authData]; + YKFCBORByteString *cborSecrect = authenticatorData.extensions.value[YKFCBORTextString(@"hmac-secret")]; + NSData *secret = cborSecrect.value; + NSData *decryptedOutputs = [secret ykf_aes256DecryptedDataWithKey: sharedSecret]; + NSData *output1 = [decryptedOutputs subdataWithRange: NSMakeRange(0, 32)]; + NSData *output2; + if (decryptedOutputs.length == 64) { + output2 = [decryptedOutputs subdataWithRange:NSMakeRange(32, 32)]; + } + NSMutableDictionary *outputDict = [NSMutableDictionary new]; + outputDict[@"first"] = [output1 ykf_websafeBase64EncodedString]; + if (output2) { + outputDict[@"seconde"] = [output2 ykf_websafeBase64EncodedString]; + } + NSMutableDictionary *resultsDict = [NSMutableDictionary new]; + resultsDict[@"results"] = outputDict; + NSMutableDictionary *prfDict = [NSMutableDictionary new]; + prfDict[@"prf"] = resultsDict; + self.extensionsOutput = prfDict; + // Signature NSData *signature = response[@(YKFFIDO2GetAssertionResponseKeySignature)]; YKFAssertReturnValue(signature, @"authenticatorGetAssertion signature is required.", NO); diff --git a/YubiKit/YubiKit/Connections/Shared/Requests/FIDO2/YKFFIDO2MakeCredentialResponse.h b/YubiKit/YubiKit/Connections/Shared/Requests/FIDO2/YKFFIDO2MakeCredentialResponse.h index 5db7d088..3ce930ac 100644 --- a/YubiKit/YubiKit/Connections/Shared/Requests/FIDO2/YKFFIDO2MakeCredentialResponse.h +++ b/YubiKit/YubiKit/Connections/Shared/Requests/FIDO2/YKFFIDO2MakeCredentialResponse.h @@ -157,6 +157,10 @@ NS_ASSUME_NONNULL_BEGIN @property (nonatomic, readonly, nullable) YKFCBORMap *extensions; + +- (instancetype)initWithData:(NSData *)data NS_DESIGNATED_INITIALIZER; + + /* Not available: instances should be created only by the library. */ diff --git a/YubiKit/YubiKit/Connections/Shared/Requests/FIDO2/YKFFIDO2MakeCredentialResponse.m b/YubiKit/YubiKit/Connections/Shared/Requests/FIDO2/YKFFIDO2MakeCredentialResponse.m index 1244a176..7ad16084 100644 --- a/YubiKit/YubiKit/Connections/Shared/Requests/FIDO2/YKFFIDO2MakeCredentialResponse.m +++ b/YubiKit/YubiKit/Connections/Shared/Requests/FIDO2/YKFFIDO2MakeCredentialResponse.m @@ -43,8 +43,6 @@ @interface YKFFIDO2AuthenticatorData() @property (nonatomic, readwrite) NSData *coseEncodedCredentialPublicKey; @property (nonatomic, readwrite) YKFCBORMap *extensions; -- (instancetype)initWithData:(NSData *)data NS_DESIGNATED_INITIALIZER; - @end @interface YKFFIDO2MakeCredentialResponse() diff --git a/YubiKit/YubiKit/Connections/Shared/Sessions/FIDO2/YKFFIDO2Session.m b/YubiKit/YubiKit/Connections/Shared/Sessions/FIDO2/YKFFIDO2Session.m index 4a5f9945..93b6d26b 100644 --- a/YubiKit/YubiKit/Connections/Shared/Sessions/FIDO2/YKFFIDO2Session.m +++ b/YubiKit/YubiKit/Connections/Shared/Sessions/FIDO2/YKFFIDO2Session.m @@ -493,7 +493,7 @@ - (void)getAssertionWithClientDataHash:(NSData *)clientDataHash ykf_safe_strong_self(); NSLog(@"%@", data.ykf_hexadecimalString); NSData *cborData = [strongSelf cborFromKeyResponseData:data]; - YKFFIDO2GetAssertionResponse *getAssertionResponse = [[YKFFIDO2GetAssertionResponse alloc] initWithCBORData:cborData]; + YKFFIDO2GetAssertionResponse *getAssertionResponse = [[YKFFIDO2GetAssertionResponse alloc] initWithCBORData:cborData sharedSecret:sharedSecret]; if (getAssertionResponse) { completion(getAssertionResponse, nil);