Skip to content

Commit

Permalink
Sign extension for make credential.
Browse files Browse the repository at this point in the history
  • Loading branch information
jensutbult committed Nov 7, 2024
1 parent 9a26bd0 commit f64da37
Show file tree
Hide file tree
Showing 2 changed files with 66 additions and 29 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,9 @@
#import "YKFFIDO2MakeCredentialResponse.h"
#import "YKFFIDO2GetAssertionResponse.h"

#import "YKFCBORDecoder.h"
#import "YKFCBOREncoder.h"

#import "YKFNSDataAdditions+Private.h"
#import "YKFSessionError+Private.h"

Expand Down Expand Up @@ -349,7 +352,13 @@ - (void)makeCredentialWithClientDataHash:(NSData *)clientDataHash
NSMutableDictionary *signExtensionDict = [NSMutableDictionary new];
// Flags hard coded for now. More information here:
// https://github.com/Yubico/python-fido2/blob/8722a8925509d3320f8cb6d8a22c76e2af08fb20/fido2/ctap2/extensions.py#L493
int flags = 0b101;

int flags;
if (options[@"userVerification"] && [options[@"userVerification"] isEqual:@"required"]) {
flags = 0b101;
} else {
flags = 0b001;
}

NSMutableArray *algorithms = [NSMutableArray array];
[(NSArray *)generateKeyDict[@"algorithms"] enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
Expand Down Expand Up @@ -405,13 +414,50 @@ - (void)makeCredentialWithClientDataHash:(NSData *)clientDataHash
} else {
extensionsClientOutput[@"prf"] = @{@"enabled" : @NO};
}
//
// NSError *error;
// NSData *jsonData = [NSJSONSerialization dataWithJSONObject:extensionsClientOutput
// options:NSJSONWritingPrettyPrinted
// error:&error];
// NSString *jsonString = [[NSString alloc] initWithData:jsonData encoding:NSUTF8StringEncoding];
// NSLog(@"%@", jsonString);
}
if (authenticatorInputs[YKFCBORTextString(@"sign")]) {
YKFCBORMap *cborMap = makeCredentialResponse.authenticatorData.extensions.value[YKFCBORTextString(@"sign")];
YKFCBORByteString *signAttestationObject = cborMap.value[YKFCBORInteger(7)];
NSData *signAttestationData = signAttestationObject.value;
if (!signAttestationData) {
[NSException raise:@"Invalid input" format:@"Invalid data."];
}
YKFCBORMap *signAttestation = nil;
NSInputStream *decoderInputStream = [[NSInputStream alloc] initWithData:signAttestationData];
[decoderInputStream open];
signAttestation = [YKFCBORDecoder decodeObjectFrom:decoderInputStream];
[decoderInputStream close];
if (!signAttestation) {
[NSException raise:@"Invalid input" format:@"Invalid data"];
}
NSData *authenticatorDataBytes = ((YKFCBORByteString *)signAttestation.value[YKFCBORInteger(2)]).value;
YKFFIDO2AuthenticatorData *authenticatorData = [[YKFFIDO2AuthenticatorData alloc] initWithData:authenticatorDataBytes];


YKFCBORMap *coseKeyCborMap = nil;
decoderInputStream = [[NSInputStream alloc] initWithData:authenticatorData.coseEncodedCredentialPublicKey];
[decoderInputStream open];
coseKeyCborMap = [YKFCBORDecoder decodeObjectFrom:decoderInputStream];
[decoderInputStream close];

NSMutableDictionary *keyHandleDict = [NSMutableDictionary new];
for (int i = 1; i < 4; i++) {
if (coseKeyCborMap.value[YKFCBORInteger(i)]) {
keyHandleDict[YKFCBORInteger(i)] = i == 1 ? YKFCBORInteger(-2) : coseKeyCborMap.value[YKFCBORInteger(i)];
}
}

NSData *keyHandleData = [YKFCBOREncoder encodeMap:YKFCBORMap(keyHandleDict)];
NSMutableDictionary *generatedKeyDict = [NSMutableDictionary new];
generatedKeyDict[@"publicKey"] = [authenticatorData.coseEncodedCredentialPublicKey ykf_websafeBase64EncodedString];
generatedKeyDict[@"keyHandle"] = [keyHandleData ykf_websafeBase64EncodedString];

NSMutableDictionary *signDict = [NSMutableDictionary new];
signDict[@"generatedKey"] = generatedKeyDict;
if (cborMap.value[YKFCBORInteger(6)]) {
signDict[@"signature"] = ((YKFCBORByteString *)cborMap.value[YKFCBORInteger(6)]).value;
}
extensionsClientOutput[@"sign"] = signDict;
}

if (makeCredentialResponse) {
Expand Down
33 changes: 12 additions & 21 deletions YubiKitTests/Tests/FIDO2Tests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -246,6 +246,18 @@ class FIDO2Tests: XCTestCase {
}
}

func testCreateSignExtensionCredential() {
runYubiKitTest { connection, completion in
connection.fido2TestSession { session in
let createExtensions = ["sign" : ["generateKey": ["algorithms": [-65539]]]]
session.addCredentialAndAssert(algorithm: YKFFIDO2PublicKeyAlgorithmES256, options: [YKFFIDO2OptionRK: false, YKFFIDO2OptionUV: false], extensions: createExtensions) { response in
print(response.authenticatorData)
print("✅ Created new FIDO2 credential: \(response)")
}
}
}
}

func testCreatePRFSecretExtensionCredential() {
runYubiKitTest { connection, completion in
connection.fido2TestSession { session in
Expand Down Expand Up @@ -285,27 +297,6 @@ class FIDO2Tests: XCTestCase {
}
}
}


func testCreateSignExtensionCredential() {
runYubiKitTest { connection, completion in
connection.fido2TestSession { session in
session.verifyPin("123456") { error in
if let error { XCTFail("verifyPin failed with: \(error)"); return }

let extensions = ["sign" : ["generateKey" : ["algorithms" : [-65539, -7]],
"phData" : "84ecfc628f1576ed179241e240db0a77f986954546adbc207e9c43e032f18450"]]
session.addCredentialAndAssert(algorithm: YKFFIDO2PublicKeyAlgorithmEdDSA, options: [YKFFIDO2OptionRK: true], extensions: extensions) { response in
print("✅ Created new FIDO2 credential: \(response)")
session.getAssertionAndAssert(response: response, options: [YKFFIDO2OptionUP: false]) { response in
print("✅ Asserted FIDO2 credential: \(response)")
completion()
}
}
}
}
}
}
}

extension YKFFIDO2Session {
Expand Down

0 comments on commit f64da37

Please sign in to comment.