| /* |
| * Copyright (C) 2019 Apple Inc. All rights reserved. |
| * |
| * Redistribution and use in source and binary forms, with or without |
| * modification, are permitted provided that the following conditions |
| * are met: |
| * 1. Redistributions of source code must retain the above copyright |
| * notice, this list of conditions and the following disclaimer. |
| * 2. Redistributions in binary form must reproduce the above copyright |
| * notice, this list of conditions and the following disclaimer in the |
| * documentation and/or other materials provided with the distribution. |
| * |
| * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' |
| * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, |
| * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
| * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS |
| * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR |
| * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF |
| * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS |
| * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN |
| * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) |
| * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF |
| * THE POSSIBILITY OF SUCH DAMAGE. |
| */ |
| |
| #import "config.h" |
| #import "MockNfcService.h" |
| |
| #if ENABLE(WEB_AUTHN) |
| #import "CtapNfcDriver.h" |
| #import "NearFieldSPI.h" |
| #import "NfcConnection.h" |
| #import <WebCore/FidoConstants.h> |
| #import <wtf/BlockPtr.h> |
| #import <wtf/RetainPtr.h> |
| #import <wtf/RunLoop.h> |
| #import <wtf/Vector.h> |
| |
| #import "NearFieldSoftLink.h" |
| |
| namespace { |
| uint8_t tagID1[] = { 0x01 }; |
| uint8_t tagID2[] = { 0x02 }; |
| } |
| |
| #if HAVE(NEAR_FIELD) |
| |
| @interface WKMockNFTag : NSObject <NFTag> |
| |
| - (instancetype)initWithType:(NFTagType)type; |
| - (instancetype)initWithType:(NFTagType)type tagID:(NSData *)tagID; |
| |
| @end |
| |
| @implementation WKMockNFTag { |
| NFTagType _type; |
| RetainPtr<NSData> _tagID; |
| } |
| |
| @synthesize technology=_technology; |
| @synthesize AppData=_AppData; |
| @synthesize UID=_UID; |
| @synthesize ndefAvailability=_ndefAvailability; |
| @synthesize ndefMessageSize=_ndefMessageSize; |
| @synthesize ndefContainerSize=_ndefContainerSize; |
| @synthesize tagA=_tagA; |
| @synthesize tagB=_tagB; |
| @synthesize tagF=_tagF; |
| |
| - (NFTagType)type |
| { |
| return _type; |
| } |
| |
| - (NSData *)tagID |
| { |
| return _tagID.get(); |
| } |
| |
| - (instancetype)initWithNFTag:(id<NFTag>)tag |
| { |
| if ((self = [super init])) { |
| _type = tag.type; |
| _tagID = tag.tagID; |
| } |
| return self; |
| } |
| |
| - (void)dealloc |
| { |
| [_AppData release]; |
| _AppData = nil; |
| [_UID release]; |
| _UID = nil; |
| |
| [super dealloc]; |
| } |
| |
| - (NSString*)description |
| { |
| return nil; |
| } |
| |
| - (BOOL)isEqualToNFTag:(id<NFTag>)tag |
| { |
| return NO; |
| } |
| |
| - (instancetype)initWithType:(NFTagType)type |
| { |
| return [self initWithType:type tagID:adoptNS([[NSData alloc] initWithBytes:tagID1 length:sizeof(tagID1)]).get()]; |
| } |
| |
| - (instancetype)initWithType:(NFTagType)type tagID:(NSData *)tagID |
| { |
| if ((self = [super init])) { |
| _type = type; |
| _tagID = tagID; |
| } |
| return self; |
| } |
| |
| @end |
| |
| #endif // HAVE(NEAR_FIELD) |
| |
| namespace WebKit { |
| using namespace fido; |
| using Mock = WebCore::MockWebAuthenticationConfiguration; |
| |
| #if HAVE(NEAR_FIELD) |
| |
| namespace { |
| |
| static id<NFReaderSessionDelegate> globalNFReaderSessionDelegate; |
| static MockNfcService* globalNfcService; |
| |
| static void NFReaderSessionSetDelegate(id, SEL, id<NFReaderSessionDelegate> delegate) |
| { |
| globalNFReaderSessionDelegate = delegate; |
| } |
| |
| static BOOL NFReaderSessionConnectTagFail(id, SEL, NFTag *) |
| { |
| return NO; |
| } |
| |
| static BOOL NFReaderSessionConnectTag(id, SEL, NFTag *) |
| { |
| return YES; |
| } |
| |
| static BOOL NFReaderSessionStopPolling(id, SEL) |
| { |
| if (!globalNfcService) |
| return NO; |
| globalNfcService->receiveStopPolling(); |
| return YES; |
| } |
| |
| static BOOL NFReaderSessionStartPollingWithError(id, SEL, NSError **) |
| { |
| if (!globalNfcService) |
| return NO; |
| globalNfcService->receiveStartPolling(); |
| return YES; |
| } |
| |
| static NSData* NFReaderSessionTransceive(id, SEL, NSData *) |
| { |
| if (!globalNfcService) |
| return nil; |
| return globalNfcService->transceive(); |
| } |
| |
| } // namespace |
| |
| #endif // HAVE(NEAR_FIELD) |
| |
| MockNfcService::MockNfcService(Observer& observer, const WebCore::MockWebAuthenticationConfiguration& configuration) |
| : NfcService(observer) |
| , m_configuration(configuration) |
| { |
| } |
| |
| NSData* MockNfcService::transceive() |
| { |
| if (m_configuration.nfc->payloadBase64.isEmpty()) |
| return nil; |
| |
| auto result = [[NSData alloc] initWithBase64EncodedString:m_configuration.nfc->payloadBase64[0] options:NSDataBase64DecodingIgnoreUnknownCharacters]; |
| m_configuration.nfc->payloadBase64.remove(0); |
| return [result autorelease]; |
| } |
| |
| void MockNfcService::receiveStopPolling() |
| { |
| // For purpose of restart polling. |
| m_configuration.nfc->multiplePhysicalTags = false; |
| } |
| |
| void MockNfcService::receiveStartPolling() |
| { |
| RunLoop::main().dispatch([weakThis = makeWeakPtr(*this)] { |
| if (!weakThis) |
| return; |
| weakThis->detectTags(); |
| }); |
| } |
| |
| void MockNfcService::platformStartDiscovery() |
| { |
| #if HAVE(NEAR_FIELD) |
| if (!!m_configuration.nfc) { |
| globalNfcService = this; |
| |
| Method methodToSwizzle1 = class_getInstanceMethod(getNFReaderSessionClass(), @selector(setDelegate:)); |
| method_setImplementation(methodToSwizzle1, (IMP)NFReaderSessionSetDelegate); |
| |
| Method methodToSwizzle2 = class_getInstanceMethod(getNFReaderSessionClass(), @selector(connectTag:)); |
| if (m_configuration.nfc->error == Mock::NfcError::NoConnections) |
| method_setImplementation(methodToSwizzle2, (IMP)NFReaderSessionConnectTagFail); |
| else |
| method_setImplementation(methodToSwizzle2, (IMP)NFReaderSessionConnectTag); |
| |
| Method methodToSwizzle3 = class_getInstanceMethod(getNFReaderSessionClass(), @selector(transceive:)); |
| method_setImplementation(methodToSwizzle3, (IMP)NFReaderSessionTransceive); |
| |
| Method methodToSwizzle4 = class_getInstanceMethod(getNFReaderSessionClass(), @selector(stopPolling)); |
| method_setImplementation(methodToSwizzle4, (IMP)NFReaderSessionStopPolling); |
| |
| Method methodToSwizzle5 = class_getInstanceMethod(getNFReaderSessionClass(), @selector(startPollingWithError:)); |
| method_setImplementation(methodToSwizzle5, (IMP)NFReaderSessionStartPollingWithError); |
| |
| auto readerSession = adoptNS([allocNFReaderSessionInstance() init]); |
| setConnection(NfcConnection::create(readerSession.get(), *this)); |
| } |
| LOG_ERROR("No nfc authenticators is available."); |
| #endif // HAVE(NEAR_FIELD) |
| } |
| |
| void MockNfcService::detectTags() const |
| { |
| #if HAVE(NEAR_FIELD) |
| if (m_configuration.nfc->error == Mock::NfcError::NoTags) |
| return; |
| |
| auto callback = makeBlockPtr([configuration = m_configuration] { |
| auto tags = adoptNS([[NSMutableArray alloc] init]); |
| if (configuration.nfc->error == Mock::NfcError::WrongTagType || configuration.nfc->multipleTags) |
| [tags addObject:adoptNS([[WKMockNFTag alloc] initWithType:NFTagTypeUnknown]).get()]; |
| else |
| [tags addObject:adoptNS([[WKMockNFTag alloc] initWithType:NFTagTypeGeneric4A]).get()]; |
| |
| if (configuration.nfc->multipleTags) |
| [tags addObject:adoptNS([[WKMockNFTag alloc] initWithType:NFTagTypeGeneric4A]).get()]; |
| |
| if (configuration.nfc->multiplePhysicalTags) |
| [tags addObject:adoptNS([[WKMockNFTag alloc] initWithType:NFTagTypeGeneric4A tagID:adoptNS([[NSData alloc] initWithBytes:tagID2 length:sizeof(tagID2)]).get()]).get()]; |
| |
| [globalNFReaderSessionDelegate readerSession:nil didDetectTags:tags.get()]; |
| }); |
| dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), callback.get()); |
| #endif // HAVE(NEAR_FIELD) |
| } |
| |
| } // namespace WebKit |
| |
| #endif // ENABLE(WEB_AUTHN) |