ca3de21022b3a3912780ba071b1fdd646dc9ba4a
[WebKit-https.git] / Source / WebKit / UIProcess / WebAuthentication / Mock / MockNfcService.mm
1 /*
2  * Copyright (C) 2019 Apple Inc. All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  * 1. Redistributions of source code must retain the above copyright
8  *    notice, this list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright
10  *    notice, this list of conditions and the following disclaimer in the
11  *    documentation and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
14  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
15  * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
17  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
18  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
19  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
20  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
21  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
22  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
23  * THE POSSIBILITY OF SUCH DAMAGE.
24  */
25
26 #import "config.h"
27 #import "MockNfcService.h"
28
29 #if ENABLE(WEB_AUTHN)
30 #import "CtapNfcDriver.h"
31 #import "NearFieldSPI.h"
32 #import "NfcConnection.h"
33 #import <WebCore/FidoConstants.h>
34 #import <wtf/BlockPtr.h>
35 #import <wtf/RetainPtr.h>
36 #import <wtf/RunLoop.h>
37 #import <wtf/Vector.h>
38
39 #import "NearFieldSoftLink.h"
40
41 #if HAVE(NEAR_FIELD)
42
43 @interface WKMockNFTag : NSObject <NFTag>
44
45 - (instancetype)initWithType:(NFTagType)type;
46
47 @end
48
49 @implementation WKMockNFTag {
50     NFTagType _type;
51 }
52
53 @synthesize technology=_technology;
54 @synthesize tagID=_tagID;
55 @synthesize AppData=_AppData;
56 @synthesize UID=_UID;
57 @synthesize ndefAvailability=_ndefAvailability;
58 @synthesize ndefMessageSize=_ndefMessageSize;
59 @synthesize ndefContainerSize=_ndefContainerSize;
60 @synthesize tagA=_tagA;
61 @synthesize tagB=_tagB;
62 @synthesize tagF=_tagF;
63
64 - (NFTagType)type
65 {
66     return _type;
67 }
68
69 - (instancetype)initWithNFTag:(id<NFTag>)tag
70 {
71     if ((self = [super init]))
72         _type = tag.type;
73     return self;
74 }
75
76 - (void)dealloc
77 {
78     [_tagID release];
79     _tagID = nil;
80     [_AppData release];
81     _AppData = nil;
82     [_UID release];
83     _UID = nil;
84
85     [super dealloc];
86 }
87
88 - (NSString*)description
89 {
90     return nil;
91 }
92
93 - (BOOL)isEqualToNFTag:(id<NFTag>)tag
94 {
95     return NO;
96 }
97
98 - (instancetype)initWithType:(NFTagType)type
99 {
100     if ((self = [super init]))
101         _type = type;
102     return self;
103 }
104
105 @end
106
107 #endif // HAVE(NEAR_FIELD)
108
109 namespace WebKit {
110 using namespace fido;
111 using MockNfc = MockWebAuthenticationConfiguration::Nfc;
112
113 #if HAVE(NEAR_FIELD)
114
115 namespace {
116
117 static id<NFReaderSessionDelegate> globalNFReaderSessionDelegate;
118 static MockNfcService* globalNfcService;
119
120 static void NFReaderSessionSetDelegate(id, SEL, id<NFReaderSessionDelegate> delegate)
121 {
122     globalNFReaderSessionDelegate = delegate;
123 }
124
125 static BOOL NFReaderSessionConnectTagFail(id, SEL, NFTag *)
126 {
127     return NO;
128 }
129
130 static BOOL NFReaderSessionConnectTag(id, SEL, NFTag *)
131 {
132     return YES;
133 }
134
135 static NSData* NFReaderSessionTransceive(id, SEL, NSData *)
136 {
137     if (!globalNfcService)
138         return nil;
139     return globalNfcService->transceive();
140 }
141
142 } // namespace
143
144 #endif // HAVE(NEAR_FIELD)
145
146 MockNfcService::MockNfcService(Observer& observer, const MockWebAuthenticationConfiguration& configuration)
147     : NfcService(observer)
148     , m_configuration(configuration)
149 {
150 }
151
152 NSData* MockNfcService::transceive()
153 {
154     if (m_configuration.nfc->payloadBase64.isEmpty())
155         return nil;
156
157     auto result = [[NSData alloc] initWithBase64EncodedString:m_configuration.nfc->payloadBase64[0] options:NSDataBase64DecodingIgnoreUnknownCharacters];
158     m_configuration.nfc->payloadBase64.remove(0);
159     return [result autorelease];
160 }
161
162 void MockNfcService::platformStartDiscovery()
163 {
164 #if HAVE(NEAR_FIELD)
165     if (!!m_configuration.nfc) {
166         globalNfcService = this;
167
168         Method methodToSwizzle1 = class_getInstanceMethod(getNFReaderSessionClass(), @selector(setDelegate:));
169         method_setImplementation(methodToSwizzle1, (IMP)NFReaderSessionSetDelegate);
170
171         Method methodToSwizzle2 = class_getInstanceMethod(getNFReaderSessionClass(), @selector(connectTag:));
172         if (m_configuration.nfc->error == MockNfc::Error::NoConnections)
173             method_setImplementation(methodToSwizzle2, (IMP)NFReaderSessionConnectTagFail);
174         else
175             method_setImplementation(methodToSwizzle2, (IMP)NFReaderSessionConnectTag);
176
177         Method methodToSwizzle3 = class_getInstanceMethod(getNFReaderSessionClass(), @selector(transceive:));
178         method_setImplementation(methodToSwizzle3, (IMP)NFReaderSessionTransceive);
179
180         auto readerSession = adoptNS([allocNFReaderSessionInstance() init]);
181         setDriver(WTF::makeUnique<CtapNfcDriver>(makeUniqueRef<NfcConnection>(readerSession.get(), *this)));
182
183         RunLoop::main().dispatch([weakThis = makeWeakPtr(*this)] {
184             if (!weakThis)
185                 return;
186             weakThis->detectTags();
187         });
188         return;
189     }
190     LOG_ERROR("No nfc authenticators is available.");
191 #endif // HAVE(NEAR_FIELD)
192 }
193
194 void MockNfcService::detectTags() const
195 {
196 #if HAVE(NEAR_FIELD)
197     if (m_configuration.nfc->error == MockNfc::Error::NoTags)
198         return;
199
200     auto callback = makeBlockPtr([configuration = m_configuration] {
201         auto tags = adoptNS([[NSMutableArray alloc] init]);
202         if (configuration.nfc->error == MockNfc::Error::WrongTagType || configuration.nfc->multipleTags)
203             [tags addObject:adoptNS([[WKMockNFTag alloc] initWithType:NFTagTypeUnknown]).get()];
204         else
205             [tags addObject:adoptNS([[WKMockNFTag alloc] initWithType:NFTagTypeGeneric4A]).get()];
206
207         if (configuration.nfc->multipleTags)
208             [tags addObject:adoptNS([[WKMockNFTag alloc] initWithType:NFTagTypeGeneric4A]).get()];
209
210         [globalNFReaderSessionDelegate readerSession:nil didDetectTags:tags.get()];
211     });
212     dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), callback.get());
213 #endif // HAVE(NEAR_FIELD)
214 }
215
216 } // namespace WebKit
217
218 #endif // ENABLE(WEB_AUTHN)