]> git.saurik.com Git - apple/security.git/blob - OSX/sec/securityd/Regressions/secd-155-otr-negotiation-monitor.m
Security-58286.51.6.tar.gz
[apple/security.git] / OSX / sec / securityd / Regressions / secd-155-otr-negotiation-monitor.m
1 //
2 // secd-155-otrnegotiationmonitor.m
3 // secdtests_ios
4 //
5 // Created by Michelle Auricchio on 6/5/17.
6 //
7
8 #import <Foundation/Foundation.h>
9 #include <Security/SecureObjectSync/SOSAccount.h>
10 #include <Security/SecureObjectSync/SOSCloudCircle.h>
11 #include <Security/SecureObjectSync/SOSInternal.h>
12 #include <Security/SecureObjectSync/SOSUserKeygen.h>
13 #include <Security/SecureObjectSync/SOSTransport.h>
14 #include <Security/SecureObjectSync/SOSViews.h>
15 #include <Security/SecureObjectSync/SOSAccountTrustClassic+Expansion.h>
16 #include <Security/SecureObjectSync/SOSTransportMessage.h>
17 #include <Security/SecureObjectSync/SOSPeerOTRTimer.h>
18
19 #import "SOSAccountTesting.h"
20 #import "SOSTransportTestTransports.h"
21
22 #include <stdlib.h>
23 #include <unistd.h>
24
25 #include "secd_regressions.h"
26 #include "SOSTestDataSource.h"
27
28 #include "SOSRegressionUtilities.h"
29 #include "SecRecoveryKey.h"
30
31 #include <utilities/SecCFWrappers.h>
32 #include <Security/SecKeyPriv.h>
33
34 #include <securityd/SOSCloudCircleServer.h>
35 #include "SecdTestKeychainUtilities.h"
36 #include "SOSAccountTesting.h"
37 #import "SOSTransportTestTransports.h"
38 #include "SOSTestDevice.h"
39 #include "SOSTestDataSource.h"
40 #include <Security/SecureObjectSync/SOSTransportMessageIDS.h>
41 #include <Security/SecureObjectSync/SOSAccountTrustClassic+Circle.h>
42 static int kTestTestCount = 114;
43
44 static bool SOSAccountIsThisPeerIDMe(SOSAccount* account, CFStringRef peerID) {
45 SOSAccountTrustClassic* trust = account.trust;
46 SOSPeerInfoRef mypi = trust.peerInfo;
47 ok(mypi);
48 CFStringRef myPeerID = SOSPeerInfoGetPeerID(mypi);
49 ok(myPeerID);
50 return myPeerID && CFEqualSafe(myPeerID, peerID);
51 }
52
53 __unused static void ids_test_sync(SOSAccount* alice_account, SOSAccount* bob_account){
54
55 CFMutableDictionaryRef changes = CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault);
56 __block bool SyncingCompletedOverIDS = false;
57 __block CFErrorRef localError = NULL;
58
59 SOSAccountTrustClassic* aliceTrust = alice_account.trust;
60 SOSAccountTrustClassic* bobTrust = bob_account.trust;
61 SOSDataSourceRef bob_ds = SOSDataSourceFactoryCreateDataSource(bob_account.factory, CFSTR("ak"), NULL);
62 SOSEngineRef bob_engine = bob_ds ? SOSDataSourceGetSharedEngine(bob_ds, NULL) : (SOSEngineRef) NULL;
63 SOSDataSourceRef alice_ds = SOSDataSourceFactoryCreateDataSource(alice_account.factory, CFSTR("ak"), NULL);
64 SOSEngineRef alice_engine = alice_ds ? SOSDataSourceGetSharedEngine(alice_ds, NULL) : (SOSEngineRef) NULL;
65
66 /* new routines to test */
67 __block NSString *bobID = bob_account.peerID;
68 __block NSString *aliceID = alice_account.peerID;
69
70
71 ///////////////////////////
72 //second exchange//
73 ///////////////////////////
74
75 SOSCircleForEachValidPeer(aliceTrust.trustedCircle, alice_account.accountKey, ^(SOSPeerInfoRef peer) {
76 if (!SOSAccountIsThisPeerIDMe(alice_account, SOSPeerInfoGetPeerID(peer))) {
77 if(SOSPeerInfoShouldUseIDSTransport(aliceTrust.peerInfo, peer) &&
78 SOSPeerInfoShouldUseIDSMessageFragmentation(aliceTrust.peerInfo, peer)){
79 secnotice("IDS Transport","Syncing with IDS capable peers using IDS!");
80
81 CFMutableSetRef ids = CFSetCreateMutableForCFTypes(kCFAllocatorDefault);
82 CFSetAddValue(ids, SOSPeerInfoGetPeerID(peer));
83
84 SyncingCompletedOverIDS = [alice_account.ids_message_transport SOSTransportMessageSyncWithPeers:alice_account.ids_message_transport p:ids err:&localError];
85 CFReleaseNull(ids);
86 }
87 }
88 });
89
90 SOSEngineWithPeerID(alice_engine, (__bridge CFStringRef)bobID, NULL, ^(SOSPeerRef bob_peer, SOSCoderRef coder, SOSDataSourceRef dataSource, SOSTransactionRef txn, bool *forceSaveState) {
91 ok(false == SOSPeerOTRTimerHaveReachedMaxRetryAllowance(alice_account , bobID));
92 ok(false == SOSPeerTimerForPeerExist(bob_peer));
93 ok(false == SOSPeerOTRTimerHaveAnRTTAvailable(alice_account, bobID));
94 ok(true == SOSCoderIsCoderInAwaitingState(coder));
95 });
96
97 ProcessChangesUntilNoChange(changes, alice_account, bob_account, NULL);
98
99 SOSCircleForEachValidPeer(bobTrust.trustedCircle, bob_account.accountKey, ^(SOSPeerInfoRef peer) {
100 if (!SOSAccountIsThisPeerIDMe(bob_account, SOSPeerInfoGetPeerID(peer))) {
101 if(SOSPeerInfoShouldUseIDSTransport(bobTrust.peerInfo, peer) &&
102 SOSPeerInfoShouldUseIDSMessageFragmentation(bobTrust.peerInfo, peer)){
103 secnotice("IDS Transport","Syncing with IDS capable peers using IDS!");
104
105 CFMutableSetRef ids = CFSetCreateMutableForCFTypes(kCFAllocatorDefault);
106 CFSetAddValue(ids, SOSPeerInfoGetPeerID(peer));
107
108 SyncingCompletedOverIDS = [(SOSMessageIDSTest*)bob_account.ids_message_transport SOSTransportMessageSyncWithPeers:(SOSMessageIDSTest*)bob_account.ids_message_transport p:ids err:&localError];
109 CFReleaseNull(ids);
110 }
111 }
112 });
113
114 ok(SyncingCompletedOverIDS, "synced items over IDS");
115
116 SOSEngineWithPeerID(bob_engine, (__bridge CFStringRef)aliceID, NULL, ^(SOSPeerRef alice_peer, SOSCoderRef coder, SOSDataSourceRef dataSource, SOSTransactionRef txn, bool *forceSaveState) {
117 ok(false == SOSPeerOTRTimerHaveReachedMaxRetryAllowance(bob_account , aliceID));
118 ok(false == SOSPeerTimerForPeerExist(alice_peer));
119 ok(false == SOSPeerOTRTimerHaveAnRTTAvailable(bob_account, aliceID));
120 });
121
122 ProcessChangesUntilNoChange(changes, alice_account, bob_account, NULL);
123
124 SOSEngineWithPeerID(alice_engine, (__bridge CFStringRef)bobID, NULL, ^(SOSPeerRef bob_peer, SOSCoderRef coder, SOSDataSourceRef dataSource, SOSTransactionRef txn, bool *forceSaveState) {
125 ok(false == SOSPeerOTRTimerHaveReachedMaxRetryAllowance(alice_account , bobID));
126 ok(false == SOSPeerTimerForPeerExist(bob_peer));
127 ok(false == SOSPeerOTRTimerHaveAnRTTAvailable(alice_account, bobID));
128 ok(true == SOSCoderIsCoderInAwaitingState(coder));
129 });
130
131 ///////////////////////////
132 //second sync exchange //
133 ///////////////////////////
134
135 SOSCircleForEachValidPeer(aliceTrust.trustedCircle, alice_account.accountKey, ^(SOSPeerInfoRef peer) {
136 if (!SOSAccountIsThisPeerIDMe(alice_account, SOSPeerInfoGetPeerID(peer))) {
137 if(SOSPeerInfoShouldUseIDSTransport(aliceTrust.peerInfo, peer) &&
138 SOSPeerInfoShouldUseIDSMessageFragmentation(aliceTrust.peerInfo, peer)){
139 secnotice("IDS Transport","Syncing with IDS capable peers using IDS!");
140
141 CFMutableSetRef ids = CFSetCreateMutableForCFTypes(kCFAllocatorDefault);
142 CFSetAddValue(ids, SOSPeerInfoGetPeerID(peer));
143
144 SyncingCompletedOverIDS = [alice_account.ids_message_transport SOSTransportMessageSyncWithPeers:alice_account.ids_message_transport p:ids err:&localError];
145 CFReleaseNull(ids);
146 }
147 }
148 });
149
150 SOSEngineWithPeerID(alice_engine, (__bridge CFStringRef)bobID, NULL, ^(SOSPeerRef bob_peer, SOSCoderRef coder, SOSDataSourceRef dataSource, SOSTransactionRef txn, bool *forceSaveState) {
151 ok(false == SOSPeerOTRTimerHaveReachedMaxRetryAllowance(alice_account , bobID));
152 ok(false == SOSPeerTimerForPeerExist(bob_peer));
153 ok(false == SOSPeerOTRTimerHaveAnRTTAvailable(alice_account, bobID));
154 ok(true == SOSCoderIsCoderInAwaitingState(coder));
155 });
156
157 ProcessChangesUntilNoChange(changes, alice_account, bob_account, NULL);
158
159 SOSCircleForEachValidPeer(bobTrust.trustedCircle, bob_account.accountKey, ^(SOSPeerInfoRef peer) {
160 if (!SOSAccountIsThisPeerIDMe(bob_account, SOSPeerInfoGetPeerID(peer))) {
161 if(SOSPeerInfoShouldUseIDSTransport(bobTrust.peerInfo, peer) &&
162 SOSPeerInfoShouldUseIDSMessageFragmentation(bobTrust.peerInfo, peer)){
163 secnotice("IDS Transport","Syncing with IDS capable peers using IDS!");
164
165 CFMutableSetRef ids = CFSetCreateMutableForCFTypes(kCFAllocatorDefault);
166 CFSetAddValue(ids, SOSPeerInfoGetPeerID(peer));
167
168 SyncingCompletedOverIDS = [(SOSMessageIDSTest*)bob_account.ids_message_transport SOSTransportMessageSyncWithPeers:(SOSMessageIDSTest*)bob_account.ids_message_transport p:ids err:&localError];
169 CFReleaseNull(ids);
170 }
171 }
172 });
173
174 ok(SyncingCompletedOverIDS, "synced items over IDS");
175
176 SOSEngineWithPeerID(bob_engine, (__bridge CFStringRef)aliceID, NULL, ^(SOSPeerRef alice_peer, SOSCoderRef coder, SOSDataSourceRef dataSource, SOSTransactionRef txn, bool *forceSaveState) {
177 ok(false == SOSPeerOTRTimerHaveReachedMaxRetryAllowance(bob_account , aliceID));
178 ok(false == SOSPeerTimerForPeerExist(alice_peer));
179 ok(false == SOSPeerOTRTimerHaveAnRTTAvailable(bob_account, aliceID));
180 });
181
182 ProcessChangesUntilNoChange(changes, alice_account, bob_account, NULL);
183
184 SOSEngineWithPeerID(alice_engine, (__bridge CFStringRef)bobID, NULL, ^(SOSPeerRef bob_peer, SOSCoderRef coder, SOSDataSourceRef dataSource, SOSTransactionRef txn, bool *forceSaveState) {
185 ok(false == SOSPeerOTRTimerHaveReachedMaxRetryAllowance(alice_account , bobID));
186 ok(false == SOSPeerTimerForPeerExist(bob_peer));
187 ok(true == SOSPeerOTRTimerHaveAnRTTAvailable(alice_account, bobID));
188 ok(false == SOSCoderIsCoderInAwaitingState(coder));
189 });
190
191 CFReleaseNull(changes);
192 }
193
194
195 static void tests(void)
196 {
197 CFErrorRef error = NULL;
198
199 CFMutableDictionaryRef changes = CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault);
200 CFDataRef cfpassword = CFDataCreate(NULL, (uint8_t *) "FooFooFoo", 10);
201 CFStringRef cfaccount = CFSTR("test@test.org");
202
203 SOSAccount* alice_account = CreateAccountForLocalChanges(CFSTR("Alice"), CFSTR("ak"));
204 SOSAccount* bob_account = CreateAccountForLocalChanges(CFSTR("Bob"), CFSTR("ak"));
205
206 ok(SOSAccountAssertUserCredentialsAndUpdate(bob_account, cfaccount, cfpassword, &error), "Credential setting (%@)", error);
207
208 // Bob wins writing at this point, feed the changes back to alice.
209 is(ProcessChangesUntilNoChange(changes, alice_account, bob_account, NULL), 1, "updates");
210
211 ok(SOSAccountAssertUserCredentialsAndUpdate(alice_account, cfaccount, cfpassword, &error), "Credential setting (%@)", error);
212 CFReleaseNull(cfpassword);
213 CFReleaseNull(error);
214
215 ok(NULL != alice_account, "Alice Created");
216 ok(NULL != bob_account, "Bob Created");
217
218 ok(SOSAccountResetToOffering_wTxn(alice_account, &error), "Reset to offering (%@)", error);
219 CFReleaseNull(error);
220
221 is(ProcessChangesUntilNoChange(changes, alice_account, bob_account, NULL), 2, "updates");
222
223 ok(SOSAccountJoinCircles_wTxn(bob_account, &error), "Bob Applies (%@)", error);
224 CFReleaseNull(error);
225
226 is(ProcessChangesUntilNoChange(changes, alice_account, bob_account, NULL), 2, "updates");
227
228 {
229 CFArrayRef applicants = SOSAccountCopyApplicants(alice_account, &error);
230
231 ok(applicants && CFArrayGetCount(applicants) == 1, "See one applicant %@ (%@)", applicants, error);
232 ok(SOSAccountAcceptApplicants(alice_account, applicants, &error), "Alice accepts (%@)", error);
233 CFReleaseNull(error);
234 CFReleaseNull(applicants);
235 }
236
237 is(ProcessChangesUntilNoChange(changes, alice_account, bob_account, NULL), 3, "updates");
238
239 accounts_agree("bob&alice pair", bob_account, alice_account);
240
241 CFArrayRef peers = SOSAccountCopyPeers(alice_account, &error);
242 ok(peers && CFArrayGetCount(peers) == 2, "See two peers %@ (%@)", peers, error);
243 CFReleaseNull(peers);
244
245 SOSDataSourceRef alice_ds = SOSDataSourceFactoryCreateDataSource(alice_account.factory, CFSTR("ak"), NULL);
246 SOSEngineRef alice_engine = alice_ds ? SOSDataSourceGetSharedEngine(alice_ds, NULL) : (SOSEngineRef) NULL;
247
248 ok(false == SOSEngineHandleCodedMessage(alice_account, alice_engine, (__bridge CFStringRef)bob_account.peerID, NULL, NULL));
249
250 SOSDataSourceRef bob_ds = SOSDataSourceFactoryCreateDataSource(bob_account.factory, CFSTR("ak"), NULL);
251 SOSEngineRef bob_engine = bob_ds ? SOSDataSourceGetSharedEngine(bob_ds, NULL) : (SOSEngineRef) NULL;
252
253 ok(false == SOSEngineHandleCodedMessage(bob_account, bob_engine, (__bridge CFStringRef)alice_account.peerID, NULL, NULL));
254
255 /* new routines to test */
256 __block NSString *bobID = bob_account.peerID;
257 __block NSString *aliceID = alice_account.peerID;
258
259 SOSEngineWithPeerID(bob_engine, (__bridge CFStringRef)aliceID, NULL, ^(SOSPeerRef alice_peer, SOSCoderRef coder, SOSDataSourceRef dataSource, SOSTransactionRef txn, bool *forceSaveState) {
260 ok(false == SOSPeerOTRTimerHaveReachedMaxRetryAllowance(bob_account , aliceID));
261 ok(false == SOSPeerTimerForPeerExist(alice_peer));
262 ok(false == SOSPeerOTRTimerHaveAnRTTAvailable(bob_account, aliceID));
263 });
264
265 SOSEngineWithPeerID(alice_engine, (__bridge CFStringRef)bobID, NULL, ^(SOSPeerRef bob_peer, SOSCoderRef coder, SOSDataSourceRef dataSource, SOSTransactionRef txn, bool *forceSaveState) {
266 ok(false == SOSPeerOTRTimerHaveReachedMaxRetryAllowance(alice_account , bobID));
267 ok(false == SOSPeerTimerForPeerExist(bob_peer));
268 ok(false == SOSPeerOTRTimerHaveAnRTTAvailable(alice_account, bobID));
269 });
270
271 //creating test devices
272 CFIndex version = 0;
273
274 // Optionally prefix each peer with name to make them more unique.
275 CFArrayRef deviceIDs = CFArrayCreateForCFTypes(kCFAllocatorDefault,alice_account.peerID, bob_account.peerID, NULL);
276 CFSetRef views = SOSViewsCopyTestV2Default();
277 CFMutableArrayRef peerMetas = CFArrayCreateMutableForCFTypes(kCFAllocatorDefault);
278 CFStringRef deviceID;
279 CFArrayForEachC(deviceIDs, deviceID) {
280 SOSPeerMetaRef peerMeta = SOSPeerMetaCreateWithComponents(deviceID, views, NULL);
281 CFArrayAppendValue(peerMetas, peerMeta);
282 CFReleaseNull(peerMeta);
283 }
284
285 CFReleaseNull(views);
286 CFArrayForEachC(deviceIDs, deviceID) {
287 SOSTestDeviceRef device = SOSTestDeviceCreateWithDbNamed(kCFAllocatorDefault, deviceID, deviceID);
288 SOSTestDeviceSetPeerIDs(device, peerMetas, version, NULL);
289
290 if([alice_account.peerID isEqual: (__bridge id)(deviceID)]){
291 alice_account.factory = device->dsf;
292 SOSTestDeviceAddGenericItem(device, CFSTR("Alice"), CFSTR("Alice-add"));
293 }
294 else{
295 bob_account.factory = device->dsf;
296 SOSTestDeviceAddGenericItem(device, CFSTR("Bob"), CFSTR("Bob-add"));
297 }
298
299 CFReleaseNull(device);
300 }
301 CFReleaseNull(deviceIDs);
302 CFReleaseNull(peerMetas);
303
304 SOSUnregisterAllTransportMessages();
305 CFArrayRemoveAllValues(message_transports);
306
307 SOSAccountTrustClassic* aliceTrust = alice_account.trust;
308 SOSAccountTrustClassic* bobTrust = bob_account.trust;
309
310 alice_account.ids_message_transport = (SOSMessageIDS*)[[SOSMessageIDSTest alloc] initWithAccount:alice_account andAccountName:CFSTR("Alice") andCircleName:SOSCircleGetName(aliceTrust.trustedCircle) err:&error ];
311
312 bob_account.ids_message_transport = (SOSMessageIDS*)[[SOSMessageIDSTest alloc] initWithAccount:bob_account andAccountName:CFSTR("Bob") andCircleName:SOSCircleGetName(bobTrust.trustedCircle) err:&error];
313
314 ok(alice_account.ids_message_transport != NULL, "Alice Account, Created IDS Test Transport");
315 ok(bob_account.ids_message_transport != NULL, "Bob Account, Created IDS Test Transport");
316
317 bool result = [alice_account.trust modifyCircle:alice_account.circle_transport err:&error action:^bool(SOSCircleRef circle) {
318 CFErrorRef localError = NULL;
319
320 SOSFullPeerInfoUpdateTransportType(aliceTrust.fullPeerInfo, SOSTransportMessageTypeIDSV2, &localError);
321 SOSFullPeerInfoUpdateTransportPreference(aliceTrust.fullPeerInfo, kCFBooleanFalse, &localError);
322 SOSFullPeerInfoUpdateTransportFragmentationPreference(aliceTrust.fullPeerInfo, kCFBooleanTrue, &localError);
323 SOSFullPeerInfoUpdateTransportAckModelPreference(aliceTrust.fullPeerInfo, kCFBooleanTrue, &localError);
324
325 return SOSCircleHasPeer(circle, aliceTrust.peerInfo, NULL);
326 }];
327
328 ok(result, "Alice account update circle with transport type");
329
330 is(ProcessChangesUntilNoChange(changes, alice_account, bob_account, NULL), 2, "updates");
331
332 result = [bob_account.trust modifyCircle:bob_account.circle_transport err:&error action:^bool(SOSCircleRef circle) {
333 CFErrorRef localError = NULL;
334
335 SOSFullPeerInfoUpdateTransportType(bobTrust.fullPeerInfo, SOSTransportMessageTypeIDSV2, &localError);
336 SOSFullPeerInfoUpdateTransportPreference(bobTrust.fullPeerInfo, kCFBooleanFalse, &localError);
337 SOSFullPeerInfoUpdateTransportFragmentationPreference(bobTrust.fullPeerInfo, kCFBooleanTrue, &localError);
338 SOSFullPeerInfoUpdateTransportAckModelPreference(bobTrust.fullPeerInfo, kCFBooleanTrue, &localError);
339
340 return SOSCircleHasPeer(circle, bobTrust.peerInfo, NULL);
341 }];
342
343 ok(result, "Bob account update circle with transport type");
344 is(ProcessChangesUntilNoChange(changes, alice_account, bob_account, NULL), 2, "updates");
345
346 CFStringRef alice_transportType =SOSPeerInfoCopyTransportType(alice_account.peerInfo);
347 CFStringRef bob_accountTransportType = SOSPeerInfoCopyTransportType(bob_account.peerInfo);
348 ok(CFEqualSafe(alice_transportType, CFSTR("IDS2.0")), "Alice transport type not IDS");
349 ok(CFEqualSafe(bob_accountTransportType, CFSTR("IDS2.0")), "Bob transport type not IDS");
350
351 CFReleaseNull(alice_transportType);
352 CFReleaseNull(bob_accountTransportType);
353
354 SOSTransportMessageIDSTestSetName((SOSMessageIDSTest*)alice_account.ids_message_transport, CFSTR("Alice Account"));
355 ok(SOSTransportMessageIDSTestGetName((SOSMessageIDSTest*)alice_account.ids_message_transport) != NULL, "retrieved getting account name");
356 ok(SOSAccountRetrieveDeviceIDFromKeychainSyncingOverIDSProxy(alice_account, &error) != false, "device ID from KeychainSyncingOverIDSProxy");
357
358 SOSTransportMessageIDSTestSetName((SOSMessageIDSTest*)bob_account.ids_message_transport, CFSTR("Bob Account"));
359 ok(SOSTransportMessageIDSTestGetName((SOSMessageIDSTest*)bob_account.ids_message_transport) != NULL, "retrieved getting account name");
360 ok(SOSAccountRetrieveDeviceIDFromKeychainSyncingOverIDSProxy(bob_account, &error) != false, "device ID from KeychainSyncingOverIDSProxy");
361
362
363 ok(SOSAccountSetMyDSID_wTxn(alice_account, CFSTR("Alice"),&error), "Setting IDS device ID");
364 CFStringRef alice_dsid = SOSAccountCopyDeviceID(alice_account, &error);
365 ok(CFEqualSafe(alice_dsid, CFSTR("Alice")), "Getting IDS device ID");
366 CFReleaseNull(alice_dsid);
367
368 ok(SOSAccountSetMyDSID_wTxn(bob_account, CFSTR("Bob"),&error), "Setting IDS device ID");
369 CFStringRef bob_dsid = SOSAccountCopyDeviceID(bob_account, &error);
370 ok(CFEqualSafe(bob_dsid, CFSTR("Bob")), "Getting IDS device ID");
371 CFReleaseNull(bob_dsid);
372 is(ProcessChangesUntilNoChange(changes, alice_account, bob_account, NULL), 3, "updates");
373
374 ok(SOSAccountEnsurePeerRegistration(alice_account, NULL), "ensure peer registration - alice");
375
376 ok(SOSAccountEnsurePeerRegistration(bob_account, NULL), "ensure peer registration - bob");
377
378 // ids_test_sync(alice_account, bob_account);
379 }
380
381 int secd_155_otr_negotiation_monitor(int argc, char *const *argv)
382 {
383 plan_tests(kTestTestCount);
384
385 secd_test_setup_temp_keychain(__FUNCTION__, NULL);
386
387 tests();
388
389 return 0;
390 }