c0c263997ec3b85ca428556eacd1ec278c88e34e
[WebKit-https.git] / Tools / TestWebKitAPI / Tests / WebKitCocoa / Challenge.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
28 #import "PlatformUtilities.h"
29 #import "TCPServer.h"
30 #import "Test.h"
31 #import "TestWKWebView.h"
32 #import "WKWebViewConfigurationExtras.h"
33 #import <WebKit/WKNavigationDelegate.h>
34 #import <WebKit/WKProcessPoolPrivate.h>
35 #import <WebKit/WKWebsiteDataRecordPrivate.h>
36 #import <WebKit/WebKit.h>
37 #import <wtf/RetainPtr.h>
38 #import <wtf/spi/cocoa/SecuritySPI.h>
39
40 static bool navigationFinished;
41
42 static RetainPtr<NSURLCredential> credentialWithIdentity()
43 {
44     auto certificateBytes = TestWebKitAPI::TCPServer::testCertificate();
45     auto certificate = adoptCF(SecCertificateCreateWithData(nullptr, (__bridge CFDataRef)[NSData dataWithBytes:certificateBytes.data() length:certificateBytes.size()]));
46     
47     auto privateKeyBytes = TestWebKitAPI::TCPServer::testPrivateKey();
48     NSData *derEncodedPrivateKey = [NSData dataWithBytes:privateKeyBytes.data() length:privateKeyBytes.size()];
49     NSDictionary* options = @{
50         (id)kSecAttrKeyType: (id)kSecAttrKeyTypeRSA,
51         (id)kSecAttrKeyClass: (id)kSecAttrKeyClassPrivate,
52         (id)kSecAttrKeySizeInBits: @4096,
53     };
54     const NSUInteger pemEncodedPrivateKeyHeaderLength = 26;
55     CFErrorRef error = nullptr;
56     auto privateKey = adoptCF(SecKeyCreateWithData((__bridge CFDataRef)[derEncodedPrivateKey subdataWithRange:NSMakeRange(pemEncodedPrivateKeyHeaderLength, derEncodedPrivateKey.length - pemEncodedPrivateKeyHeaderLength)], (__bridge CFDictionaryRef)options, &error));
57     EXPECT_NULL(error);
58     EXPECT_NOT_NULL(privateKey.get());
59
60     auto identity = adoptCF(SecIdentityCreate(kCFAllocatorDefault, certificate.get(), privateKey.get()));
61     EXPECT_NOT_NULL(identity);
62     
63     return [NSURLCredential credentialWithIdentity:identity.get() certificates:@[(id)certificate.get()] persistence:NSURLCredentialPersistenceNone];
64 }
65
66 @interface ChallengeDelegate : NSObject <WKNavigationDelegate>
67 @end
68
69 @implementation ChallengeDelegate
70
71 - (void)webView:(WKWebView *)webView didFinishNavigation:(null_unspecified WKNavigation *)navigation
72 {
73     navigationFinished = true;
74 }
75
76 - (void)webView:(WKWebView *)webView didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition, NSURLCredential *))completionHandler
77 {
78     NSURLProtectionSpace *protectionSpace = challenge.protectionSpace;
79     EXPECT_NULL(challenge.proposedCredential);
80     EXPECT_EQ(challenge.previousFailureCount, 0);
81     EXPECT_TRUE([protectionSpace.realm isEqualToString:@"testrealm"]);
82     EXPECT_FALSE(protectionSpace.receivesCredentialSecurely);
83     EXPECT_FALSE(protectionSpace.isProxy);
84     EXPECT_TRUE([protectionSpace.host isEqualToString:@"127.0.0.1"]);
85     EXPECT_NULL(protectionSpace.proxyType);
86     EXPECT_TRUE([protectionSpace.protocol isEqualToString:NSURLProtectionSpaceHTTP]);
87     EXPECT_TRUE([protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodHTTPBasic]);
88     EXPECT_EQ([(NSHTTPURLResponse *)challenge.failureResponse statusCode], 401);
89
90     completionHandler(NSURLSessionAuthChallengeUseCredential, credentialWithIdentity().get());
91 }
92
93 @end
94
95 TEST(Challenge, SecIdentity)
96 {
97     using namespace TestWebKitAPI;
98     TCPServer server(TCPServer::respondWithChallengeThenOK);
99
100     auto webView = adoptNS([WKWebView new]);
101     auto delegate = adoptNS([ChallengeDelegate new]);
102     [webView setNavigationDelegate:delegate.get()];
103
104     // Make sure no credential left by previous tests.
105     NSURLProtectionSpace *protectionSpace = [[[NSURLProtectionSpace alloc] initWithHost:@"127.0.0.1" port:server.port() protocol:NSURLProtectionSpaceHTTP realm:@"testrealm" authenticationMethod:NSURLAuthenticationMethodHTTPBasic] autorelease];
106     [[webView configuration].processPool _clearPermanentCredentialsForProtectionSpace:protectionSpace];
107
108     [webView loadRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:[NSString stringWithFormat:@"http://127.0.0.1:%d/", server.port()]]]];
109
110     Util::run(&navigationFinished);
111 }
112
113 @interface ClientCertificateDelegate : NSObject <WKNavigationDelegate> {
114     Vector<RetainPtr<NSString>> _authenticationMethods;
115 }
116 - (const Vector<RetainPtr<NSString>>&)authenticationMethods;
117 @end
118
119 @implementation ClientCertificateDelegate
120
121 - (void)webView:(WKWebView *)webView didFinishNavigation:(null_unspecified WKNavigation *)navigation
122 {
123     navigationFinished = true;
124 }
125
126 - (void)webView:(WKWebView *)webView didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition, NSURLCredential *))completionHandler
127 {
128     _authenticationMethods.append(challenge.protectionSpace.authenticationMethod);
129
130     if ([challenge.protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust])
131         return completionHandler(NSURLSessionAuthChallengeUseCredential, [NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust]);
132     
133     EXPECT_TRUE([challenge.protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodClientCertificate]);
134     completionHandler(NSURLSessionAuthChallengeUseCredential, credentialWithIdentity().get());
135 }
136
137 - (const Vector<RetainPtr<NSString>>&)authenticationMethods
138 {
139     return _authenticationMethods;
140 }
141
142 @end
143
144 #if HAVE(SEC_KEY_PROXY)
145 TEST(Challenge, ClientCertificate)
146 {
147     using namespace TestWebKitAPI;
148     TCPServer server(TCPServer::Protocol::HTTPSWithClientCertificateRequest, TCPServer::respondWithOK);
149
150     auto webView = adoptNS([WKWebView new]);
151     auto delegate = adoptNS([ClientCertificateDelegate new]);
152     [webView setNavigationDelegate:delegate.get()];
153     [webView loadRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:[NSString stringWithFormat:@"https://127.0.0.1:%d/", server.port()]]]];
154     
155     Util::run(&navigationFinished);
156     auto& methods = [delegate authenticationMethods];
157     EXPECT_EQ(methods.size(), 2ull);
158     EXPECT_TRUE([methods[0] isEqualToString:NSURLAuthenticationMethodServerTrust]);
159     EXPECT_TRUE([methods[1] isEqualToString:NSURLAuthenticationMethodClientCertificate]);
160 }
161 #endif
162
163 static bool receivedSecondChallenge;
164 static RetainPtr<NSURLCredential> persistentCredential;
165
166 @interface ProposedCredentialDelegate : NSObject <WKNavigationDelegate>
167 @end
168
169 @implementation ProposedCredentialDelegate
170
171 - (void)webView:(WKWebView *)webView didFinishNavigation:(null_unspecified WKNavigation *)navigation
172 {
173     navigationFinished = true;
174 }
175
176 - (void)webView:(WKWebView *)webView didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition, NSURLCredential *))completionHandler
177 {
178     static bool firstChallenge = true;
179     if (firstChallenge) {
180         firstChallenge = false;
181         persistentCredential = adoptNS([[NSURLCredential alloc] initWithUser:@"testuser" password:@"testpassword" persistence:NSURLCredentialPersistencePermanent]);
182         return completionHandler(NSURLSessionAuthChallengeUseCredential, persistentCredential.get());
183         
184     }
185     receivedSecondChallenge = true;
186     return completionHandler(NSURLSessionAuthChallengeUseCredential, nil);
187 }
188
189 @end
190
191 TEST(Challenge, BasicProposedCredential)
192 {
193     using namespace TestWebKitAPI;
194     TCPServer server(TCPServer::respondWithChallengeThenOK, 2);
195     auto configuration = retainPtr([WKWebViewConfiguration _test_configurationWithTestPlugInClassName:@"BasicProposedCredentialPlugIn"]);
196     auto webView = adoptNS([[WKWebView alloc] initWithFrame:CGRectZero configuration:configuration.get()]);
197     auto delegate = adoptNS([ProposedCredentialDelegate new]);
198     [webView setNavigationDelegate:delegate.get()];
199
200     // Make sure no credential left by previous tests.
201     NSURLProtectionSpace *protectionSpace = [[[NSURLProtectionSpace alloc] initWithHost:@"127.0.0.1" port:server.port() protocol:NSURLProtectionSpaceHTTP realm:@"testrealm" authenticationMethod:NSURLAuthenticationMethodHTTPBasic] autorelease];
202     [[webView configuration].processPool _clearPermanentCredentialsForProtectionSpace:protectionSpace];
203
204     RetainPtr<NSURLRequest> request = [NSURLRequest requestWithURL:[NSURL URLWithString:[NSString stringWithFormat:@"http://127.0.0.1:%d/", server.port()]]];
205     [webView loadRequest:request.get()];
206     Util::run(&navigationFinished);
207     navigationFinished = false;
208     [webView loadRequest:request.get()];
209     Util::run(&navigationFinished);
210     EXPECT_TRUE(receivedSecondChallenge);
211
212     // Clear persistent credentials created by this test.
213     [[webView configuration].processPool _clearPermanentCredentialsForProtectionSpace:protectionSpace];
214 }
215
216 #if HAVE(SSL)
217 static void verifyCertificateAndPublicKey(SecTrustRef trust)
218 {
219     EXPECT_NOT_NULL(trust);
220
221     auto compareData = [] (const RetainPtr<CFDataRef>& data, const Vector<uint8_t>& expected) {
222         size_t length = CFDataGetLength(data.get());
223         EXPECT_EQ(length, expected.size());
224         const UInt8* bytes = CFDataGetBytePtr(data.get());
225         for (size_t i = 0; i < length; ++i)
226             EXPECT_EQ(expected[i], bytes[i]);
227     };
228
229     auto publicKey = adoptCF(SecKeyCopyExternalRepresentation(adoptCF(SecTrustCopyPublicKey(trust)).get(), nullptr));
230     compareData(publicKey, {
231         0x30, 0x81, 0x89, 0x02, 0x81, 0x81, 0x00, 0xd8, 0x2b, 0xc8, 0xa6, 0x32, 0xe4, 0x62, 0xff, 0x4d,
232         0xf3, 0xd0, 0xad, 0x59, 0x8b, 0x45, 0xa7, 0xbd, 0xf1, 0x47, 0xbf, 0x09, 0x58, 0x7b, 0x22, 0xbd,
233         0x35, 0xae, 0x97, 0x25, 0x86, 0x94, 0xa0, 0x80, 0xc0, 0xb4, 0x1f, 0x76, 0x91, 0x67, 0x46, 0x31,
234         0xd0, 0x10, 0x84, 0xb7, 0x22, 0x1e, 0x70, 0x23, 0x91, 0x72, 0xc8, 0xe9, 0x6d, 0x79, 0x3a, 0x85,
235         0x77, 0x80, 0x0f, 0xc4, 0x95, 0x16, 0x75, 0xc5, 0x4a, 0x71, 0x4c, 0xc8, 0x63, 0x3f, 0xa3, 0xf2,
236         0x63, 0x9c, 0x2a, 0x4f, 0x9a, 0xfa, 0xcb, 0xc1, 0x71, 0x6e, 0x28, 0x85, 0x28, 0xa0, 0x27, 0x1e,
237         0x65, 0x1c, 0xae, 0x07, 0xd5, 0x5b, 0x6f, 0x2d, 0x43, 0xed, 0x2b, 0x90, 0xb1, 0x8c, 0xaf, 0x24,
238         0x6d, 0xae, 0xe9, 0x17, 0x3a, 0x05, 0xc1, 0xbf, 0xb8, 0x1c, 0xae, 0x65, 0x3b, 0x1b, 0x58, 0xc2,
239         0xd9, 0xae, 0xd6, 0xaa, 0x67, 0x88, 0xf1, 0x02, 0x03, 0x01, 0x00, 0x01
240     });
241     
242     EXPECT_EQ(1, SecTrustGetCertificateCount(trust));
243     auto certificate = adoptCF(SecCertificateCopyData(SecTrustGetCertificateAtIndex(trust, 0)));
244     compareData(certificate, {
245         0x30, 0x82, 0x02, 0x58, 0x30, 0x82, 0x01, 0xc1, 0xa0, 0x03, 0x02, 0x01, 0x02, 0x02, 0x09, 0x00,
246         0xfb, 0xb0, 0x4c, 0x2e, 0xab, 0x10, 0x9b, 0x0c, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86,
247         0xf7, 0x0d, 0x01, 0x01, 0x05, 0x05, 0x00, 0x30, 0x45, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55,
248         0x04, 0x06, 0x13, 0x02, 0x41, 0x55, 0x31, 0x13, 0x30, 0x11, 0x06, 0x03, 0x55, 0x04, 0x08, 0x0c,
249         0x0a, 0x53, 0x6f, 0x6d, 0x65, 0x2d, 0x53, 0x74, 0x61, 0x74, 0x65, 0x31, 0x21, 0x30, 0x1f, 0x06,
250         0x03, 0x55, 0x04, 0x0a, 0x0c, 0x18, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x20, 0x57,
251         0x69, 0x64, 0x67, 0x69, 0x74, 0x73, 0x20, 0x50, 0x74, 0x79, 0x20, 0x4c, 0x74, 0x64, 0x30, 0x1e,
252         0x17, 0x0d, 0x31, 0x34, 0x30, 0x34, 0x32, 0x33, 0x32, 0x30, 0x35, 0x30, 0x34, 0x30, 0x5a, 0x17,
253         0x0d, 0x31, 0x37, 0x30, 0x34, 0x32, 0x32, 0x32, 0x30, 0x35, 0x30, 0x34, 0x30, 0x5a, 0x30, 0x45,
254         0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x41, 0x55, 0x31, 0x13, 0x30,
255         0x11, 0x06, 0x03, 0x55, 0x04, 0x08, 0x0c, 0x0a, 0x53, 0x6f, 0x6d, 0x65, 0x2d, 0x53, 0x74, 0x61,
256         0x74, 0x65, 0x31, 0x21, 0x30, 0x1f, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x0c, 0x18, 0x49, 0x6e, 0x74,
257         0x65, 0x72, 0x6e, 0x65, 0x74, 0x20, 0x57, 0x69, 0x64, 0x67, 0x69, 0x74, 0x73, 0x20, 0x50, 0x74,
258         0x79, 0x20, 0x4c, 0x74, 0x64, 0x30, 0x81, 0x9f, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86,
259         0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00, 0x03, 0x81, 0x8d, 0x00, 0x30, 0x81, 0x89, 0x02, 0x81,
260         0x81, 0x00, 0xd8, 0x2b, 0xc8, 0xa6, 0x32, 0xe4, 0x62, 0xff, 0x4d, 0xf3, 0xd0, 0xad, 0x59, 0x8b,
261         0x45, 0xa7, 0xbd, 0xf1, 0x47, 0xbf, 0x09, 0x58, 0x7b, 0x22, 0xbd, 0x35, 0xae, 0x97, 0x25, 0x86,
262         0x94, 0xa0, 0x80, 0xc0, 0xb4, 0x1f, 0x76, 0x91, 0x67, 0x46, 0x31, 0xd0, 0x10, 0x84, 0xb7, 0x22,
263         0x1e, 0x70, 0x23, 0x91, 0x72, 0xc8, 0xe9, 0x6d, 0x79, 0x3a, 0x85, 0x77, 0x80, 0x0f, 0xc4, 0x95,
264         0x16, 0x75, 0xc5, 0x4a, 0x71, 0x4c, 0xc8, 0x63, 0x3f, 0xa3, 0xf2, 0x63, 0x9c, 0x2a, 0x4f, 0x9a,
265         0xfa, 0xcb, 0xc1, 0x71, 0x6e, 0x28, 0x85, 0x28, 0xa0, 0x27, 0x1e, 0x65, 0x1c, 0xae, 0x07, 0xd5,
266         0x5b, 0x6f, 0x2d, 0x43, 0xed, 0x2b, 0x90, 0xb1, 0x8c, 0xaf, 0x24, 0x6d, 0xae, 0xe9, 0x17, 0x3a,
267         0x05, 0xc1, 0xbf, 0xb8, 0x1c, 0xae, 0x65, 0x3b, 0x1b, 0x58, 0xc2, 0xd9, 0xae, 0xd6, 0xaa, 0x67,
268         0x88, 0xf1, 0x02, 0x03, 0x01, 0x00, 0x01, 0xa3, 0x50, 0x30, 0x4e, 0x30, 0x1d, 0x06, 0x03, 0x55,
269         0x1d, 0x0e, 0x04, 0x16, 0x04, 0x14, 0x8b, 0x75, 0xd5, 0xac, 0xcb, 0x08, 0xbe, 0x0e, 0x1f, 0x65,
270         0xb7, 0xfa, 0x56, 0xbe, 0x6c, 0xa7, 0x75, 0xda, 0x85, 0xaf, 0x30, 0x1f, 0x06, 0x03, 0x55, 0x1d,
271         0x23, 0x04, 0x18, 0x30, 0x16, 0x80, 0x14, 0x8b, 0x75, 0xd5, 0xac, 0xcb, 0x08, 0xbe, 0x0e, 0x1f,
272         0x65, 0xb7, 0xfa, 0x56, 0xbe, 0x6c, 0xa7, 0x75, 0xda, 0x85, 0xaf, 0x30, 0x0c, 0x06, 0x03, 0x55,
273         0x1d, 0x13, 0x04, 0x05, 0x30, 0x03, 0x01, 0x01, 0xff, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48,
274         0x86, 0xf7, 0x0d, 0x01, 0x01, 0x05, 0x05, 0x00, 0x03, 0x81, 0x81, 0x00, 0x3b, 0xe8, 0x78, 0x6d,
275         0x95, 0xd6, 0x3d, 0x6a, 0xf7, 0x13, 0x19, 0x2c, 0x1b, 0xc2, 0x88, 0xae, 0x22, 0xab, 0xf4, 0x8d,
276         0x32, 0xf5, 0x7c, 0x71, 0x67, 0xcf, 0x2d, 0xd1, 0x1c, 0xc2, 0xc3, 0x87, 0xe2, 0xe9, 0xbe, 0x89,
277         0x5c, 0xe4, 0x34, 0xab, 0x48, 0x91, 0xc2, 0x3f, 0x95, 0xae, 0x2b, 0x47, 0x9e, 0x25, 0x78, 0x6b,
278         0x4f, 0x9a, 0x10, 0xa4, 0x72, 0xfd, 0xcf, 0xf7, 0x02, 0x0c, 0xb0, 0x0a, 0x08, 0xa4, 0x5a, 0xe2,
279         0xe5, 0x74, 0x7e, 0x11, 0x1d, 0x39, 0x60, 0x6a, 0xc9, 0x1f, 0x69, 0xf3, 0x2e, 0x63, 0x26, 0xdc,
280         0x9e, 0xef, 0x6b, 0x7a, 0x0a, 0xe1, 0x54, 0x57, 0x98, 0xaa, 0x72, 0x91, 0x78, 0x04, 0x7e, 0x1f,
281         0x8f, 0x65, 0x4d, 0x1f, 0x0b, 0x12, 0xac, 0x9c, 0x24, 0x0f, 0x84, 0x14, 0x1a, 0x55, 0x2d, 0x1f,
282         0xbb, 0xf0, 0x9d, 0x09, 0xb2, 0x08, 0x5c, 0x59, 0x32, 0x65, 0x80, 0x26
283     });
284 }
285
286 @interface ServerTrustDelegate : NSObject <WKNavigationDelegate>
287 @end
288
289 @implementation ServerTrustDelegate
290
291 - (void)webView:(WKWebView *)webView didFinishNavigation:(null_unspecified WKNavigation *)navigation
292 {
293     navigationFinished = true;
294 }
295
296 - (void)webView:(WKWebView *)webView didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition disposition, NSURLCredential * _Nullable credential))completionHandler
297 {
298     SecTrustRef trust = challenge.protectionSpace.serverTrust;
299     verifyCertificateAndPublicKey(trust);
300     completionHandler(NSURLSessionAuthChallengeUseCredential, [NSURLCredential credentialForTrust:trust]);
301 }
302
303 @end
304
305 namespace TestWebKitAPI {
306
307 TEST(WebKit, ServerTrust)
308 {
309     TCPServer server(TCPServer::Protocol::HTTPS, [] (SSL* ssl) {
310         TCPServer::read(ssl);
311
312         const char* reply = ""
313         "HTTP/1.1 200 OK\r\n"
314         "Content-Length: 13\r\n\r\n"
315         "Hello, World!";
316         TCPServer::write(ssl, reply, strlen(reply));
317     });
318
319     auto webView = adoptNS([WKWebView new]);
320     auto delegate = adoptNS([ServerTrustDelegate new]);
321     [webView setNavigationDelegate:delegate.get()];
322
323     [webView loadRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:[NSString stringWithFormat:@"https://localhost:%d/", server.port()]]]];
324     TestWebKitAPI::Util::run(&navigationFinished);
325
326     verifyCertificateAndPublicKey([webView serverTrust]);
327 }
328
329 } // namespace TestWebKitAPI
330
331 #endif // HAVE(SSL)