2 #import <Security/Security.h>
3 #import <Security/SecItemPriv.h>
4 #import "OSX/sec/Security/SecItemShim.h"
6 #import "OSX/utilities/SecCFRelease.h"
7 #import "utilities/debugging.h"
9 #import "SecEscrowPendingRecord+KeychainSupport.h"
10 #import "keychain/categories/NSError+UsefulConstructors.h"
12 @implementation SecEscrowPendingRecord (KeychainSupport)
14 - (BOOL)saveToKeychain:(NSError**)error
16 NSMutableDictionary* query = [@{
17 (id)kSecClass : (id)kSecClassInternetPassword,
18 (id)kSecAttrAccessible: (id)kSecAttrAccessibleWhenUnlockedThisDeviceOnly,
19 (id)kSecAttrAccessGroup: @"com.apple.sbd",
20 (id)kSecAttrServer: @"escrow-prerecord",
21 (id)kSecAttrDescription: [NSString stringWithFormat:@"Escrow Prerecord: %@", self.uuid],
22 (id)kSecAttrAccount: self.uuid,
23 (id)kSecValueData : self.data,
24 (id)kSecAttrIsInvisible: @YES,
25 (id)kSecUseDataProtectionKeychain : @YES,
26 (id)kSecAttrSynchronizable : @NO,
29 CFTypeRef result = NULL;
30 OSStatus status = SecItemAdd((__bridge CFDictionaryRef)query, &result);
32 NSError* localerror = nil;
34 // Did SecItemAdd fall over due to an existing item?
35 if(status == errSecDuplicateItem) {
36 // Add every primary key attribute to this find dictionary
37 NSMutableDictionary* findQuery = [[NSMutableDictionary alloc] init];
38 findQuery[(id)kSecClass] = query[(id)kSecClass];
39 findQuery[(id)kSecAttrSynchronizable] = query[(id)kSecAttrSynchronizable];
40 findQuery[(id)kSecAttrSyncViewHint] = query[(id)kSecAttrSyncViewHint];
41 findQuery[(id)kSecAttrAccessGroup] = query[(id)kSecAttrAccessGroup];
42 findQuery[(id)kSecAttrAccount] = query[(id)kSecAttrAccount];
43 findQuery[(id)kSecAttrServer] = query[(id)kSecAttrServer];
44 findQuery[(id)kSecAttrPath] = query[(id)kSecAttrPath];
45 findQuery[(id)kSecUseDataProtectionKeychain] = query[(id)kSecUseDataProtectionKeychain];
47 NSMutableDictionary* updateQuery = [query mutableCopy];
48 updateQuery[(id)kSecClass] = nil;
50 status = SecItemUpdate((__bridge CFDictionaryRef)findQuery, (__bridge CFDictionaryRef)updateQuery);
53 localerror = [NSError errorWithDomain:NSOSStatusErrorDomain
55 description:[NSString stringWithFormat:@"SecItemUpdate: %d", (int)status]];
57 } else if(status != 0) {
58 localerror = [NSError errorWithDomain:NSOSStatusErrorDomain
60 description:[NSString stringWithFormat:@"SecItemAdd: %d", (int)status]];
73 - (BOOL)deleteFromKeychain:(NSError**)error
75 NSMutableDictionary* query = [@{
76 (id)kSecClass : (id)kSecClassInternetPassword,
77 (id)kSecAttrAccessGroup: @"com.apple.sbd",
78 (id)kSecAttrServer: @"escrow-prerecord",
79 (id)kSecAttrAccount: self.uuid,
80 (id)kSecUseDataProtectionKeychain : @YES,
81 (id)kSecAttrSynchronizable : @NO,
84 OSStatus status = SecItemDelete((__bridge CFDictionaryRef)query);
86 if(status != errSecSuccess) {
88 *error = [NSError errorWithDomain:NSOSStatusErrorDomain
90 description:[NSString stringWithFormat:@"SecItemAdd: %d", (int)status]];;
98 + (SecEscrowPendingRecord* _Nullable)loadFromKeychain:(NSString*)uuid error:(NSError**)error
100 NSMutableDictionary* query = [@{
101 (id)kSecClass : (id)kSecClassInternetPassword,
102 (id)kSecAttrAccessGroup: @"com.apple.sbd",
103 (id)kSecAttrServer: @"escrow-prerecord",
104 (id)kSecAttrAccount: uuid,
105 (id)kSecMatchLimit : (id)kSecMatchLimitOne,
106 (id)kSecAttrSynchronizable : @NO,
107 (id)kSecUseDataProtectionKeychain : @YES,
108 (id)kSecReturnAttributes: @YES,
109 (id)kSecReturnData: @YES,
112 CFTypeRef result = NULL;
113 OSStatus status = SecItemCopyMatching((__bridge CFDictionaryRef)query, &result);
116 CFReleaseNull(result);
119 *error = [NSError errorWithDomain:NSOSStatusErrorDomain
121 userInfo:@{NSLocalizedDescriptionKey:
122 [NSString stringWithFormat:@"SecItemCopyMatching: %d", (int)status]}];
127 NSDictionary* resultDict = (NSDictionary*) CFBridgingRelease(result);
128 SecEscrowPendingRecord* record = [[SecEscrowPendingRecord alloc] initWithData:resultDict[(id)kSecValueData]];
130 //TODO: if no record, add an error here
135 + (NSArray<SecEscrowPendingRecord*>* _Nullable)loadAllFromKeychain:(NSError**)error
137 NSMutableDictionary* query = [@{
138 (id)kSecClass : (id)kSecClassInternetPassword,
139 (id)kSecAttrAccessGroup: @"com.apple.sbd",
140 (id)kSecAttrServer: @"escrow-prerecord",
141 (id)kSecMatchLimit : (id)kSecMatchLimitAll,
142 (id)kSecAttrSynchronizable : @NO,
143 (id)kSecUseDataProtectionKeychain : @YES,
144 (id)kSecReturnAttributes: @YES,
145 (id)kSecReturnData: @YES,
148 CFTypeRef result = NULL;
149 OSStatus status = SecItemCopyMatching((__bridge CFDictionaryRef)query, &result);
152 CFReleaseNull(result);
155 *error = [NSError errorWithDomain:NSOSStatusErrorDomain
157 userInfo:@{NSLocalizedDescriptionKey:
158 [NSString stringWithFormat:@"SecItemCopyMatching: %d", (int)status]}];
163 NSMutableArray<SecEscrowPendingRecord*>* records = [NSMutableArray array];
164 NSDictionary* resultArray = CFBridgingRelease(result);
166 for(NSDictionary* item in resultArray) {
167 SecEscrowPendingRecord* record = [[SecEscrowPendingRecord alloc] initWithData:item[(id)kSecValueData]];
169 [records addObject: record];
171 secerror("escrowrequest: Unable to deserialize keychain item");
180 @implementation SecEscrowPendingRecord (EscrowAttemptTimeout)
181 - (BOOL)escrowAttemptedWithinLastSeconds:(NSTimeInterval)timeInterval
183 NSDate* limitDate = [NSDate dateWithTimeIntervalSinceNow:-timeInterval];
184 uint64_t limitMillis = [limitDate timeIntervalSince1970] * 1000;
186 if(self.hasLastEscrowAttemptTime && self.lastEscrowAttemptTime >= limitMillis) {