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