]> git.saurik.com Git - apple/security.git/blob - keychain/ckks/CKKSAnalyticsLogger.m
a87a5d084261c5232a51e87824c40a6cd420e999
[apple/security.git] / keychain / ckks / CKKSAnalyticsLogger.m
1 /*
2 * Copyright (c) 2017 Apple Inc. All Rights Reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
11 * file.
12 *
13 * The Original Code and all software distributed under the License are
14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
20 *
21 * @APPLE_LICENSE_HEADER_END@
22 */
23
24 #if OCTAGON
25
26 #import "CKKSAnalyticsLogger.h"
27 #import "debugging.h"
28 #import "CKKS.h"
29 #import "CKKSViewManager.h"
30 #import "CKKSKeychainView.h"
31 #include <utilities/SecFileLocations.h>
32 #import "Analytics/SFAnalyticsLogger.h"
33 #import <os/log.h>
34
35 NSString* const CKKSAnalyticsAttributeRecoverableError = @"recoverableError";
36 NSString* const CKKSAnalyticsAttributeZoneName = @"zone";
37 NSString* const CKKSAnalyticsAttributeErrorDomain = @"errorDomain";
38 NSString* const CKKSAnalyticsAttributeErrorCode = @"errorCode";
39
40 NSString* const CKKSAnalyticsHasTLKs = @"TLKs";
41 NSString* const CKKSAnalyticsSyncedClassARecently = @"inSyncA";
42 NSString* const CKKSAnalyticsSyncedClassCRecently = @"inSyncC";
43 NSString* const CKKSAnalyticsIncomingQueueIsErrorFree = @"IQNOE";
44 NSString* const CKKSAnalyticsOutgoingQueueIsErrorFree = @"OQNOE";
45 NSString* const CKKSAnalyticsInSync = @"inSync";
46
47 CKKSAnalyticsFailableEvent* const CKKSEventProcessIncomingQueueClassA = (CKKSAnalyticsFailableEvent*)@"CKKSEventProcessIncomingQueueClassA";
48 CKKSAnalyticsFailableEvent* const CKKSEventProcessIncomingQueueClassC = (CKKSAnalyticsFailableEvent*)@"CKKSEventProcessIncomingQueueClassC";
49 CKKSAnalyticsFailableEvent* const CKKSEventUploadChanges = (CKKSAnalyticsFailableEvent*)@"CKKSEventUploadChanges";
50
51 CKKSAnalyticsSignpostEvent* const CKKSEventPushNotificationReceived = (CKKSAnalyticsSignpostEvent*)@"CKKSEventPushNotificationReceived";
52 CKKSAnalyticsSignpostEvent* const CKKSEventItemAddedToOutgoingQueue = (CKKSAnalyticsSignpostEvent*)@"CKKSEventItemAddedToOutgoingQueue";
53
54 @implementation CKKSAnalyticsLogger
55
56 + (NSString*)databasePath
57 {
58 return [(__bridge_transfer NSURL*)SecCopyURLForFileInKeychainDirectory((__bridge CFStringRef)@"ckks_analytics_v2.db") path];
59 }
60
61 + (instancetype)logger
62 {
63 // just here because I want it in the header for discoverability
64 return [super logger];
65 }
66
67 - (void)logSuccessForEvent:(CKKSAnalyticsFailableEvent*)event inView:(CKKSKeychainView*)view
68 {
69 [self logSuccessForEventNamed:[NSString stringWithFormat:@"%@-%@", view.zoneName, event]];
70 [self setDateProperty:[NSDate date] forKey:[NSString stringWithFormat:@"last_success_%@-%@", view.zoneName, event]];
71 }
72
73 - (void)logRecoverableError:(NSError*)error forEvent:(CKKSAnalyticsFailableEvent*)event inView:(CKKSKeychainView*)view
74 {
75 NSDictionary* attributes = @{ CKKSAnalyticsAttributeRecoverableError : @(YES),
76 CKKSAnalyticsAttributeZoneName : view.zoneName,
77 CKKSAnalyticsAttributeErrorDomain : error.domain,
78 CKKSAnalyticsAttributeErrorCode : @(error.code) };
79
80 [super logSoftFailureForEventNamed:event withAttributes:attributes];
81 }
82
83 - (void)logUnrecoverableError:(NSError*)error forEvent:(CKKSAnalyticsFailableEvent*)event inView:(CKKSKeychainView*)view
84 {
85 NSDictionary* attributes = @{ CKKSAnalyticsAttributeRecoverableError : @(NO),
86 CKKSAnalyticsAttributeZoneName : view.zoneName,
87 CKKSAnalyticsAttributeErrorDomain : error.domain,
88 CKKSAnalyticsAttributeErrorCode : @(error.code) };
89
90 [self logHardFailureForEventNamed:event withAttributes:attributes];
91 }
92
93 - (void)noteEvent:(CKKSAnalyticsSignpostEvent*)event inView:(CKKSKeychainView*)view
94 {
95 [self noteEventNamed:[NSString stringWithFormat:@"%@-%@", view.zoneName, event]];
96 }
97
98 - (NSDate*)dateOfLastSuccessForEvent:(CKKSAnalyticsFailableEvent*)event inView:(CKKSKeychainView*)view
99 {
100 return [self datePropertyForKey:[NSString stringWithFormat:@"last_success_%@-%@", view.zoneName, event]];
101 }
102
103 - (NSDictionary*)extraValuesToUploadToServer
104 {
105 NSMutableDictionary* values = [NSMutableDictionary dictionary];
106 for (NSString* viewName in [CKKSViewManager viewList]) {
107 CKKSKeychainView* view = [CKKSViewManager findOrCreateView:viewName];
108 NSDate* dateOfLastSyncClassA = [self dateOfLastSuccessForEvent:CKKSEventProcessIncomingQueueClassA inView:view];
109 NSDate* dateOfLastSyncClassC = [self dateOfLastSuccessForEvent:CKKSEventProcessIncomingQueueClassC inView:view];
110
111 NSInteger fuzzyDaysSinceClassASync = [CKKSAnalyticsLogger fuzzyDaysSinceDate:dateOfLastSyncClassA];
112 NSInteger fuzzyDaysSinceClassCSync = [CKKSAnalyticsLogger fuzzyDaysSinceDate:dateOfLastSyncClassC];
113 [values setValue:@(fuzzyDaysSinceClassASync) forKey:[NSString stringWithFormat:@"%@-daysSinceClassASync", viewName]];
114 [values setValue:@(fuzzyDaysSinceClassCSync) forKey:[NSString stringWithFormat:@"%@-daysSinceClassCSync", viewName]];
115
116 BOOL hasTLKs = [view.keyHierarchyState isEqualToString:SecCKKSZoneKeyStateReady];
117 BOOL syncedClassARecently = fuzzyDaysSinceClassASync < 7;
118 BOOL syncedClassCRecently = fuzzyDaysSinceClassCSync < 7;
119 BOOL incomingQueueIsErrorFree = view.lastIncomingQueueOperation.error == nil;
120 BOOL outgoingQueueIsErrorFree = view.lastOutgoingQueueOperation.error == nil;
121
122 NSString* hasTLKsKey = [NSString stringWithFormat:@"%@-%@", viewName, CKKSAnalyticsHasTLKs];
123 NSString* syncedClassARecentlyKey = [NSString stringWithFormat:@"%@-%@", viewName, CKKSAnalyticsSyncedClassARecently];
124 NSString* syncedClassCRecentlyKey = [NSString stringWithFormat:@"%@-%@", viewName, CKKSAnalyticsSyncedClassCRecently];
125 NSString* incomingQueueIsErrorFreeKey = [NSString stringWithFormat:@"%@-%@", viewName, CKKSAnalyticsIncomingQueueIsErrorFree];
126 NSString* outgoingQueueIsErrorFreeKey = [NSString stringWithFormat:@"%@-%@", viewName, CKKSAnalyticsOutgoingQueueIsErrorFree];
127
128 values[hasTLKsKey] = @(hasTLKs);
129 values[syncedClassARecentlyKey] = @(syncedClassARecently);
130 values[syncedClassCRecentlyKey] = @(syncedClassCRecently);
131 values[incomingQueueIsErrorFreeKey] = @(incomingQueueIsErrorFree);
132 values[outgoingQueueIsErrorFreeKey] = @(outgoingQueueIsErrorFree);
133
134 BOOL weThinkWeAreInSync = hasTLKs && syncedClassARecently && syncedClassCRecently && incomingQueueIsErrorFree && outgoingQueueIsErrorFree;
135 NSString* inSyncKey = [NSString stringWithFormat:@"%@-%@", viewName, CKKSAnalyticsInSync];
136 values[inSyncKey] = @(weThinkWeAreInSync);
137 }
138
139 return values;
140 }
141
142 @end
143
144 #endif // OCTAGON