]> git.saurik.com Git - apple/security.git/blob - keychain/ckks/CKKSReencryptOutgoingItemsOperation.m
Security-58286.51.6.tar.gz
[apple/security.git] / keychain / ckks / CKKSReencryptOutgoingItemsOperation.m
1 /*
2 * Copyright (c) 2016 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 #import "keychain/ckks/CKKS.h"
25 #import "keychain/ckks/CKKSKeychainView.h"
26 #import "keychain/ckks/CKKSCurrentKeyPointer.h"
27 #import "keychain/ckks/CKKSKey.h"
28 #import "keychain/ckks/CKKSOutgoingQueueEntry.h"
29 #import "keychain/ckks/CKKSReencryptOutgoingItemsOperation.h"
30 #import "keychain/ckks/CKKSItemEncrypter.h"
31 #import "keychain/ckks/CloudKitCategories.h"
32
33 #if OCTAGON
34
35 // Note: reencryption is not, strictly speaking, a CloudKit operation. However, we preserve this to pass it back to the outgoing queue operation we'll create
36 @interface CKKSReencryptOutgoingItemsOperation ()
37 @property CKOperationGroup* ckoperationGroup;
38 @end
39
40 @implementation CKKSReencryptOutgoingItemsOperation
41
42 - (instancetype)init {
43 return nil;
44 }
45 - (instancetype)initWithCKKSKeychainView:(CKKSKeychainView*)ckks ckoperationGroup:(CKOperationGroup*)ckoperationGroup {
46 if(self = [super init]) {
47 _ckks = ckks;
48 _ckoperationGroup = ckoperationGroup;
49
50 [self addNullableDependency:ckks.keyStateReadyDependency];
51 [self addNullableDependency:ckks.holdReencryptOutgoingItemsOperation];
52
53 // We also depend on the key hierarchy being reasonable
54 [self addNullableDependency:ckks.keyStateReadyDependency];
55
56 }
57 return self;
58 }
59
60 - (void) main {
61 CKKSKeychainView* ckks = self.ckks;
62 if(!ckks) {
63 ckkserror("ckksreencrypt", ckks, "no CKKS object");
64 return;
65 }
66
67 [ckks dispatchSync: ^bool{
68 if(self.cancelled) {
69 ckksnotice("ckksreencrypt", ckks, "CKKSReencryptOutgoingItemsOperation cancelled, quitting");
70 return false;
71 }
72
73 ckks.lastReencryptOutgoingItemsOperation = self;
74
75 NSError* error = nil;
76 bool newItems = false;
77
78 NSArray<CKKSOutgoingQueueEntry*>* oqes = [CKKSOutgoingQueueEntry allInState: SecCKKSStateReencrypt zoneID:ckks.zoneID error:&error];
79 if(error) {
80 ckkserror("ckksreencrypt", ckks, "Error fetching oqes from database: %@", error);
81 self.error = error;
82 return false;
83 }
84
85 for(CKKSOutgoingQueueEntry* oqe in oqes) {
86 // If there's already a 'new' item replacing this one, drop the reencryption on the floor
87 CKKSOutgoingQueueEntry* newOQE = [CKKSOutgoingQueueEntry tryFromDatabase:oqe.uuid state:SecCKKSStateNew zoneID:oqe.item.zoneID error:&error];
88 if(error) {
89 ckkserror("ckksreencrypt", ckks, "Couldn't load 'new' OQE to determine status: %@", error);
90 self.error = error;
91 error = nil;
92 continue;
93 }
94 if(newOQE) {
95 ckksnotice("ckksreencrypt", ckks, "Have a new OQE superceding %@ (%@), skipping", oqe, newOQE);
96 // Don't use the state transition here, either, since this item isn't really changing states
97 [oqe deleteFromDatabase:&error];
98 if(error) {
99 ckkserror("ckksreencrypt", ckks, "Couldn't delete reencrypting OQE(%@) from database: %@", oqe, error);
100 self.error = error;
101 error = nil;
102 continue;
103 }
104 continue;
105 }
106
107 ckksnotice("ckksreencrypt", ckks, "Reencrypting item %@", oqe);
108
109 NSDictionary* item = [CKKSItemEncrypter decryptItemToDictionary: oqe.item error:&error];
110 if(error) {
111 if ([error.domain isEqualToString:@"securityd"] && error.code == errSecItemNotFound) {
112 ckkserror("ckksreencrypt", ckks, "Coudn't find key in keychain; attempting to poke key hierarchy: %@", error);
113 [ckks.pokeKeyStateMachineScheduler trigger];
114 } else {
115 ckkserror("ckksreencrypt", ckks, "Couldn't decrypt item %@: %@", oqe, error);
116 }
117 self.error = error;
118 error = nil;
119 continue;
120 }
121
122 // Pick a key whose class matches the keyclass that this item
123 CKKSKey* originalKey = [CKKSKey fromDatabase: oqe.item.parentKeyUUID zoneID:ckks.zoneID error:&error];
124 if(error) {
125 ckkserror("ckksreencrypt", ckks, "Couldn't fetch key (%@) for item %@: %@", oqe.item.parentKeyUUID, oqe, error);
126 self.error = error;
127 error = nil;
128 continue;
129 }
130
131 CKKSKey* newkey = [CKKSKey currentKeyForClass: originalKey.keyclass zoneID:ckks.zoneID error:&error];
132 [newkey ensureKeyLoaded: &error];
133 if(error) {
134 ckkserror("ckksreencrypt", ckks, "Couldn't fetch the current key for class %@: %@", originalKey.keyclass, error);
135 self.error = error;
136 error = nil;
137 continue;
138 }
139
140 CKKSMirrorEntry* ckme = [CKKSMirrorEntry tryFromDatabase:oqe.item.uuid zoneID:ckks.zoneID error:&error];
141 if(error) {
142 ckkserror("ckksreencrypt", ckks, "Couldn't fetch ckme (%@) for item %@: %@", oqe.item.parentKeyUUID, oqe, error);
143 self.error = error;
144 error = nil;
145 continue;
146 }
147
148 CKKSItem* encryptedItem = [CKKSItemEncrypter encryptCKKSItem:oqe.item
149 dataDictionary:item
150 updatingCKKSItem:ckme.item
151 parentkey:newkey
152 error:&error];
153
154 if(error) {
155 ckkserror("ckksreencrypt", ckks, "Couldn't encrypt under the new key %@: %@", newkey, error);
156 self.error = error;
157 error = nil;
158 continue;
159 }
160
161 CKKSOutgoingQueueEntry* replacement = [[CKKSOutgoingQueueEntry alloc] initWithCKKSItem:encryptedItem
162 action:oqe.action
163 state:SecCKKSStateNew
164 waitUntil:nil
165 accessGroup:oqe.accessgroup];
166
167 // Don't use the CKKSKeychainView state change here, since we're doing a wholesale item swap.
168 [oqe deleteFromDatabase:&error];
169 [replacement saveToDatabase:&error];
170 if(error) {
171 ckkserror("ckksreencrypt", ckks, "Couldn't save newly-encrypted oqe %@: %@", replacement, error);
172 self.error = error;
173 error = nil;
174 continue;
175 }
176
177 newItems = true;
178 }
179
180 if(newItems) {
181 [ckks processOutgoingQueue:self.ckoperationGroup];
182 }
183 return true;
184 }];
185 }
186
187 @end;
188
189 #endif