@@ -808,5 +808,108 @@ - (void)testGetWPJKeysWithTenantId_whenEccRegistrationWithTransportKey_shouldRet
808808 XCTAssertTrue (result.privateTransportKeyRef != NULL );
809809}
810810
811+ - (void )testGetWPJKeysWithTenantId_whenEccRegistrationWithMissingTransportKey_shouldReturnOnlyDeviceKey
812+ {
813+ // Insert device key and cert but skip transport key
814+ SecCertificateRef certRef = [self dummyEccCertRef: kDummyTenant1CertIdentifier ];
815+ NSString *tag = [NSString stringWithFormat: @" %@ #%@%@ " , kMSIDPrivateKeyIdentifier , self .tenantId, @" -EC" ];
816+ SecKeyRef keyRef = [self createAndGetdummyEccPrivateKey: NO privateKeyTag: tag];
817+ NSString *keychainGroup = [self keychainGroup: NO ];
818+
819+ OSStatus status = [self insertDummyDRSIdentityIntoKeychain: certRef
820+ privateKeyRef: keyRef
821+ privateKeyTag: tag
822+ accessGroup: keychainGroup];
823+ XCTAssertTrue (status == errSecSuccess || status == errSecDuplicateItem);
824+
825+ // Don't insert transport key - simulate missing STK scenario
826+
827+ MSIDWPJKeyPairWithCert *result = [MSIDWorkPlaceJoinUtil getWPJKeysWithTenantId: self .tenantId context: nil ];
828+
829+ XCTAssertNotNil (result);
830+ XCTAssertEqual (result.keyChainVersion , MSIDWPJKeychainAccessGroupV2);
831+ XCTAssertTrue (result.privateKeyRef != NULL );
832+ XCTAssertTrue (result.privateTransportKeyRef == NULL , @" Expected privateTransportKeyRef to be nil when transport key is missing" );
833+ }
834+
835+ - (void )testGetWPJKeysWithTenantId_whenPrimaryEccRegistrationWithTransportKey_shouldReturnCorrectKeys
836+ {
837+ [self addPrimaryEccDefaultRegistrationForTenantId: self .tenantId
838+ sharedAccessGroup: [self keychainGroup: NO ]
839+ certIdentifier: kDummyTenant1CertIdentifier
840+ useSecureEnclave: YES ];
841+ [self insertEccStkKeyForTenantIdentifier: self .tenantId];
842+
843+ MSIDWPJKeyPairWithCert *result = [MSIDWorkPlaceJoinUtil getWPJKeysWithTenantId: nil context: nil ];
844+
845+ XCTAssertNotNil (result);
846+ XCTAssertEqual (result.keyChainVersion , MSIDWPJKeychainAccessGroupV2);
847+ XCTAssertTrue (result.privateKeyRef != NULL , @" Primary registration should have device key" );
848+ XCTAssertTrue (result.privateTransportKeyRef != NULL , @" Primary registration should have transport key" );
849+ }
850+
851+ - (void )testGetWPJKeysWithTenantId_whenLegacyRegistration_shouldHaveNoTransportKey
852+ {
853+ [self insertDummyWPJInLegacyFormat: YES tenantIdentifier: self .tenantId writeTenantMetadata: YES certIdentifier: kDummyTenant1CertIdentifier ];
854+
855+ MSIDWPJKeyPairWithCert *result = [MSIDWorkPlaceJoinUtil getWPJKeysWithTenantId: self .tenantId context: nil ];
856+
857+ XCTAssertNotNil (result);
858+ XCTAssertEqual (result.keyChainVersion , MSIDWPJKeychainAccessGroupV1);
859+ XCTAssertTrue (result.privateKeyRef != NULL , @" Legacy registration should have device key" );
860+ XCTAssertTrue (result.privateTransportKeyRef == NULL , @" Legacy registration should not have transport key" );
861+ }
862+
863+ - (void )testGetWPJKeysWithTenantId_whenRSARegistrationInV2Format_shouldNotHaveTransportKey
864+ {
865+ // For iOS, RSA keys in V2 format should not have transport keys
866+ // This tests the case where we have RSA device key but no transport key expected
867+
868+ [self insertDummyWPJInLegacyFormat: NO tenantIdentifier: @" tenantId" writeTenantMetadata: YES certIdentifier: kDummyTenant1CertIdentifier ];
869+
870+ MSIDWPJKeyPairWithCert *result = [MSIDWorkPlaceJoinUtil getWPJKeysWithTenantId: @" tenantId" context: nil ];
871+
872+ // For RSA registrations, transport key should be nil even in V2 format
873+ XCTAssertNotNil (result);
874+ XCTAssertEqual (result.keyChainVersion , MSIDWPJKeychainAccessGroupV2);
875+ XCTAssertTrue (result.privateKeyRef != NULL , @" Expected privateKeyRef to be non-nil for RSA registration in V2 format" );
876+ XCTAssertTrue (result.privateTransportKeyRef == NULL , @" Expected privateTransportKeyRef to be nil for RSA registration in V2 format" );
877+ }
878+
879+ - (void )testGetWPJKeysWithTenantId_concurrentAccess_shouldBeThreadSafe
880+ {
881+ [self insertDummyEccRegistrationForTenantIdentifier: self .tenantId certIdentifier: kDummyTenant1CertIdentifier useSecureEnclave: YES ];
882+ [self insertEccStkKeyForTenantIdentifier: self .tenantId];
883+ dispatch_group_t group = dispatch_group_create ();
884+ __block NSMutableArray *results = [NSMutableArray array ];
885+ __block NSLock *lock = [[NSLock alloc ] init ];
886+
887+ // Launch multiple concurrent requests
888+ for (int i = 0 ; i < 5 ; i++) {
889+ dispatch_group_async (group, dispatch_get_global_queue (DISPATCH_QUEUE_PRIORITY_DEFAULT, 0 ), ^{
890+ MSIDWPJKeyPairWithCert *result = [MSIDWorkPlaceJoinUtil getWPJKeysWithTenantId: self .tenantId context: nil ];
891+
892+ [lock lock ];
893+ if (result) {
894+ [results addObject: result];
895+ }
896+ [lock unlock ];
897+ });
898+ }
899+
900+ // Wait for all requests to complete
901+ dispatch_group_wait (group, dispatch_time (DISPATCH_TIME_NOW, 5 * NSEC_PER_SEC));
902+
903+ // All requests should succeed
904+ XCTAssertTrue (results.count == 5 , @" All concurrent requests should succeed" );
905+
906+ // Verify all results have transport keys
907+ for (MSIDWPJKeyPairWithCert *result in results) {
908+ XCTAssertTrue (result.privateKeyRef != NULL );
909+ XCTAssertTrue (result.privateTransportKeyRef != NULL );
910+ }
911+
912+ }
913+
811914@end
812915#endif
0 commit comments