blob: 98cd74584f5a8143a43992e69d698a07f20e3158 [file] [log] [blame]
/*
* 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)