]> git.saurik.com Git - apple/security.git/blob - keychain/ckks/CKKSManifest.m
Security-58286.200.222.tar.gz
[apple/security.git] / keychain / ckks / CKKSManifest.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 #if OCTAGON
25
26 #import "CKKSManifest.h"
27 #import "CKKSManifestLeafRecord.h"
28 #import "CKKS.h"
29 #import "CKKSItem.h"
30 #import "CKKSCurrentItemPointer.h"
31 #import "utilities/der_plist.h"
32 #import <securityd/SOSCloudCircleServer.h>
33 #import <securityd/SecItemServer.h>
34 #import <Security/SecureObjectSync/SOSPeerInfo.h>
35 #import <Security/SecureObjectSync/SOSCloudCircleInternal.h>
36 #import <SecurityFoundation/SFSigningOperation.h>
37 #import <SecurityFoundation/SFKey.h>
38 #import <SecurityFoundation/SFKey_Private.h>
39 #import <SecurityFoundation/SFDigestOperation.h>
40 #import <CloudKit/CloudKit.h>
41
42 NSString* const CKKSManifestZoneKey = @"zone";
43 NSString* const CKKSManifestSignerIDKey = @"signerID";
44 NSString* const CKKSManifestGenCountKey = @"gencount";
45
46 static NSString* const CKKSManifestDigestKey = @"CKKSManifestDigestKey";
47 static NSString* const CKKSManifestPeerManifestsKey = @"CKKSManifestPeerManifestsKey";
48 static NSString* const CKKSManifestCurrentItemsKey = @"CKKSManifestCurrentItemsKey";
49 static NSString* const CKKSManifestGenerationCountKey = @"CKKSManifestGenerationCountKey";
50 static NSString* const CKKSManifestSchemaVersionKey = @"CKKSManifestSchemaVersionKey";
51
52 static NSString* const CKKSManifestEC384SignatureKey = @"CKKSManifestEC384SignatureKey";
53
54 static NSString* const CKKSManifestErrorDomain = @"CKKSManifestErrorDomain";
55
56 #define NUM_MANIFEST_LEAF_RECORDS 72
57 #define BITS_PER_UUID_CHAR 36
58
59 static CKKSManifestInjectionPointHelper* __egoHelper = nil;
60 static NSMutableDictionary<NSString*, CKKSManifestInjectionPointHelper*>* __helpersDict = nil;
61 static BOOL __ignoreChanges = NO;
62
63 enum {
64 CKKSManifestErrorInvalidDigest = 1,
65 CKKSManifestErrorVerifyingKeyNotFound = 2,
66 CKKSManifestErrorManifestGenerationFailed = 3,
67 CKKSManifestErrorCurrentItemUUIDNotFound = 4
68 };
69
70 typedef NS_ENUM(NSInteger, CKKSManifestFieldType) {
71 CKKSManifestFieldTypeStringRaw = 0,
72 CKKSManifestFieldTypeStringBase64Encoded = 1,
73 CKKSManifestFieldTypeDataAsBase64String = 2,
74 CKKSManifestFieldTypeNumber = 3,
75 CKKSManifestFieldTypeArrayRaw = 4,
76 CKKSManifestFieldTypeArrayAsDERBase64String = 5,
77 CKKSManifestFieldTypeDictionaryAsDERBase64String = 6
78 };
79
80 @interface CKKSAccountInfo : NSObject {
81 SFECKeyPair* _signingKey;
82 NSDictionary* _peerVerifyingKeys;
83 NSString* _egoPeerID;
84 NSError* _setupError;
85 }
86
87 @property SFECKeyPair* signingKey;
88 @property NSDictionary* peerVerifyingKeys;
89 @property NSString* egoPeerID;
90 @property NSError* setupError;
91 @end
92
93 static NSDictionary* __thisBuildsSchema = nil;
94 static CKKSAccountInfo* s_accountInfo = nil;
95
96 @interface CKKSManifest () {
97 @package
98 NSData* _derData;
99 NSData* _digestValue;
100 NSUInteger _generationCount;
101 NSString* _signerID;
102 NSString* _zoneName;
103 NSArray* _leafRecordIDs;
104 NSArray* _peerManifestIDs;
105 NSMutableDictionary* _currentItemsDict;
106 NSDictionary* _futureData;
107 NSDictionary* _signaturesDict;
108 NSDictionary* _schema;
109 CKKSManifestInjectionPointHelper* _helper;
110 }
111
112 @property (nonatomic, readonly) NSString* zoneName;
113 @property (nonatomic, readonly) NSArray<NSString*>* leafRecordIDs;
114 @property (nonatomic, readonly) NSArray<NSString*>* peerManifestIDs;
115 @property (nonatomic, readonly) NSDictionary* currentItems;
116 @property (nonatomic, readonly) NSDictionary* futureData;
117 @property (nonatomic, readonly) NSDictionary* signatures;
118 @property (nonatomic, readonly) NSDictionary* schema;
119
120 @property (nonatomic, readwrite) NSString* signerID;
121 @property (nonatomic) CKKSManifestInjectionPointHelper* helper;
122
123 + (NSData*)digestValueForLeafRecords:(NSArray*)leafRecords;
124
125 - (void)clearDigest;
126
127 @end
128
129 @interface CKKSPendingManifest () {
130 NSMutableArray* _committedLeafRecordIDs;
131 }
132
133 @property (nonatomic, readonly) NSArray<NSString*>* committedLeafRecordIDs;
134
135 @end
136
137 @interface CKKSEgoManifest () {
138 NSArray* _leafRecords;
139 }
140
141 @property (class, readonly) CKKSManifestInjectionPointHelper* egoHelper;
142
143 @property (nonatomic, readwrite) NSDictionary* signatures;
144
145 @end
146
147 @interface CKKSManifestInjectionPointHelper ()
148
149 - (instancetype)initWithPeerID:(NSString*)peerID keyPair:(SFECKeyPair*)keyPair isEgoPeer:(BOOL)isEgoPeer;
150
151 - (void)performWithSigningKey:(void (^)(SFECKeyPair* _Nullable signingKey, NSError* _Nullable error))handler;
152 - (void)performWithEgoPeerID:(void (^)(NSString* _Nullable egoPeerID, NSError* _Nullable error))handler;
153 - (void)performWithPeerVerifyingKeys:(void (^)(NSDictionary<NSString*, SFECPublicKey*>* _Nullable peerKeys, NSError* _Nullable error))handler;
154
155 @end
156
157 static NSData* ManifestDERData(NSString* zone, NSData* digestValue, NSArray<NSString*>* peerManifestIDs, NSDictionary<NSString*, NSString*>* currentItems, NSUInteger generationCount, NSDictionary* futureFields, NSDictionary* schema, NSError** error)
158 {
159 NSArray* sortedPeerManifestIDs = [peerManifestIDs sortedArrayUsingSelector:@selector(compare:)];
160
161 NSMutableDictionary* manifestDict = [NSMutableDictionary dictionary];
162 manifestDict[CKKSManifestDigestKey] = digestValue;
163 manifestDict[CKKSManifestPeerManifestsKey] = sortedPeerManifestIDs;
164 manifestDict[CKKSManifestCurrentItemsKey] = currentItems;
165 manifestDict[CKKSManifestGenerationCountKey] = [NSNumber numberWithUnsignedInteger:generationCount];
166
167 [futureFields enumerateKeysAndObjectsUsingBlock:^(NSString* futureKey, id futureValue, BOOL* stop) {
168 CKKSManifestFieldType fieldType = [schema[futureKey] integerValue];
169 if (fieldType == CKKSManifestFieldTypeStringRaw) {
170 manifestDict[futureKey] = futureValue;
171 }
172 else if (fieldType == CKKSManifestFieldTypeStringBase64Encoded) {
173 manifestDict[futureKey] = [[NSString alloc] initWithData:[[NSData alloc] initWithBase64EncodedString:futureValue options:0] encoding:NSUTF8StringEncoding];
174 }
175 else if (fieldType == CKKSManifestFieldTypeDataAsBase64String) {
176 manifestDict[futureKey] = [[NSData alloc] initWithBase64EncodedString:futureValue options:0];
177 }
178 else if (fieldType == CKKSManifestFieldTypeNumber) {
179 manifestDict[futureKey] = futureValue;
180 }
181 else if (fieldType == CKKSManifestFieldTypeArrayRaw) {
182 manifestDict[futureKey] = futureValue;
183 }
184 else if (fieldType == CKKSManifestFieldTypeArrayAsDERBase64String) {
185 manifestDict[futureKey] = (__bridge_transfer NSArray*)CFPropertyListCreateWithDERData(NULL, (__bridge CFDataRef)[[NSData alloc] initWithBase64EncodedData:futureValue options:0], 0, NULL, NULL);
186 }
187 else if (fieldType == CKKSManifestFieldTypeDictionaryAsDERBase64String) {
188 manifestDict[futureKey] = (__bridge_transfer NSDictionary*)CFPropertyListCreateWithDERData(NULL, (__bridge CFDataRef)[[NSData alloc] initWithBase64EncodedData:futureValue options:0], 0, NULL, NULL);
189 }
190 else {
191 ckkserrorwithzonename("ckksmanifest", zone, "unrecognized field type in future schema: %ld", (long)fieldType);
192 }
193 }];
194
195 CFErrorRef cfError = NULL;
196 NSData* derData = (__bridge_transfer NSData*)CFPropertyListCreateDERData(NULL, (__bridge CFDictionaryRef)manifestDict, &cfError);
197 if (cfError) {
198 ckkserrorwithzonename("ckksmanifest", zone, "error creating manifest der data: %@", cfError);
199 if (error) {
200 *error = (__bridge_transfer NSError*)cfError;
201 }
202 return nil;
203 }
204
205 return derData;
206 }
207
208 static NSUInteger LeafBucketIndexForUUID(NSString* uuid)
209 {
210 NSInteger prefixIntegerValue = 0;
211 for (NSInteger characterIndex = 0; characterIndex * BITS_PER_UUID_CHAR < NUM_MANIFEST_LEAF_RECORDS; characterIndex++) {
212 prefixIntegerValue += [uuid characterAtIndex:characterIndex];
213 }
214
215 return prefixIntegerValue % NUM_MANIFEST_LEAF_RECORDS;
216 }
217
218 @implementation CKKSManifest
219
220 @synthesize zoneName = _zoneName;
221 @synthesize leafRecordIDs = _leafRecordIDs;
222 @synthesize peerManifestIDs = _peerManifestIDs;
223 @synthesize currentItems = _currentItemsDict;
224 @synthesize futureData = _futureData;
225 @synthesize signatures = _signaturesDict;
226 @synthesize signerID = _signerID;
227 @synthesize schema = _schema;
228 @synthesize helper = _helper;
229
230 + (void)initialize
231 {
232 if (self == [CKKSManifest class]) {
233 __thisBuildsSchema = @{ CKKSManifestSchemaVersionKey : @(1),
234 SecCKRecordManifestDigestValueKey : @(CKKSManifestFieldTypeDataAsBase64String),
235 SecCKRecordManifestGenerationCountKey : @(CKKSManifestFieldTypeNumber),
236 SecCKRecordManifestLeafRecordIDsKey : @(CKKSManifestFieldTypeArrayRaw),
237 SecCKRecordManifestPeerManifestRecordIDsKey : @(CKKSManifestFieldTypeArrayRaw),
238 SecCKRecordManifestCurrentItemsKey : @(CKKSManifestFieldTypeDictionaryAsDERBase64String),
239 SecCKRecordManifestSignaturesKey : @(CKKSManifestFieldTypeDictionaryAsDERBase64String),
240 SecCKRecordManifestSignerIDKey : @(CKKSManifestFieldTypeStringRaw),
241 SecCKRecordManifestSchemaKey : @(CKKSManifestFieldTypeDictionaryAsDERBase64String) };
242 }
243 }
244
245 + (void)loadDefaults
246 {
247 static dispatch_once_t onceToken;
248 dispatch_once(&onceToken, ^{
249 NSDictionary* systemDefaults = [NSDictionary dictionaryWithContentsOfFile:[[NSBundle bundleWithPath:@"/System/Library/Frameworks/Security.framework"] pathForResource:@"CKKSLogging" ofType:@"plist"]];
250 bool shouldSync = !![[systemDefaults valueForKey:@"SyncManifests"] boolValue];
251 bool shouldEnforce = !![[systemDefaults valueForKey:@"EnforceManifests"] boolValue];
252
253 NSUserDefaults* defaults = [[NSUserDefaults alloc] initWithSuiteName:SecCKKSUserDefaultsSuite];
254 bool userDefaultsShouldSync = !![defaults boolForKey:@"SyncManifests"];
255 bool userDefaultsShouldEnforce = !![defaults boolForKey:@"EnforceManifests"];
256 shouldSync |= userDefaultsShouldSync;
257 shouldEnforce |= userDefaultsShouldEnforce;
258
259 if(shouldSync) {
260 SecCKKSEnableSyncManifests();
261 }
262 if(shouldEnforce) {
263 SecCKKSEnableEnforceManifests();
264 }
265 });
266 }
267
268 + (bool)shouldSyncManifests
269 {
270 [self loadDefaults];
271 return SecCKKSSyncManifests();
272 }
273
274 + (bool)shouldEnforceManifests
275 {
276 [self loadDefaults];
277 return SecCKKSEnforceManifests();
278 }
279
280 + (void)performWithAccountInfo:(void (^)(void))action
281 {
282 CKKSAccountInfo* accountInfo = [[CKKSAccountInfo alloc] init];
283
284 [[CKKSEgoManifest egoHelper] performWithSigningKey:^(SFECKeyPair* signingKey, NSError* error) {
285 accountInfo.signingKey = signingKey;
286 if(error) {
287 secerror("ckksmanifest: cannot get signing key from account: %@", error);
288 if(accountInfo.setupError == nil) {
289 accountInfo.setupError = error;
290 }
291 }
292 }];
293
294 [[CKKSEgoManifest egoHelper] performWithEgoPeerID:^(NSString* egoPeerID, NSError* error) {
295 accountInfo.egoPeerID = egoPeerID;
296
297 if(error) {
298 secerror("ckksmanifest: cannot get ego peer ID from account: %@", error);
299 if(accountInfo.setupError == nil) {
300 accountInfo.setupError = error;
301 }
302 }
303 }];
304
305 [[CKKSEgoManifest egoHelper] performWithPeerVerifyingKeys:^(NSDictionary<NSString*, SFECPublicKey*>* peerKeys, NSError* error) {
306 accountInfo.peerVerifyingKeys = peerKeys;
307 if(error) {
308 secerror("ckksmanifest: cannot get peer keys from account: %@", error);
309 if(accountInfo.setupError == nil) {
310 accountInfo.setupError = error;
311 }
312 }
313 }];
314
315 s_accountInfo = accountInfo;
316
317 action();
318
319 s_accountInfo = nil;
320 }
321
322 + (nullable instancetype)tryFromDatabaseWhere:(NSDictionary*)whereDict error:(NSError* __autoreleasing *)error
323 {
324 CKKSManifest* manifest = [super tryFromDatabaseWhere:whereDict error:error];
325 manifest.helper = __helpersDict[manifest.signerID];
326 return manifest;
327 }
328
329 + (nullable instancetype)manifestForZone:(NSString*)zone peerID:(NSString*)peerID error:(NSError**)error
330 {
331 NSDictionary* databaseWhereClause = @{ @"ckzone" : zone, @"signerID" : peerID };
332 return [self tryFromDatabaseWhere:databaseWhereClause error:error];
333 }
334
335 + (nullable instancetype)manifestForRecordName:(NSString*)recordName error:(NSError**)error
336 {
337 return [self tryFromDatabaseWhere:[self whereClauseForRecordName:recordName] error:error];
338 }
339
340 + (nullable instancetype)latestTrustedManifestForZone:(NSString*)zone error:(NSError**)error
341 {
342 NSDictionary* databaseWhereClause = @{ @"ckzone" : zone };
343 NSArray* manifests = [[self allWhere:databaseWhereClause error:error] sortedArrayUsingComparator:^NSComparisonResult(CKKSManifest* _Nonnull firstManifest, CKKSManifest* _Nonnull secondManifest) {
344 NSInteger firstGenerationCount = firstManifest.generationCount;
345 NSInteger secondGenerationCount = secondManifest.generationCount;
346
347 if (firstGenerationCount > secondGenerationCount) {
348 return NSOrderedDescending;
349 }
350 else if (firstGenerationCount < secondGenerationCount) {
351 return NSOrderedAscending;
352 }
353 else {
354 return NSOrderedSame;
355 }
356 }];
357
358 __block CKKSManifest* result = nil;
359 [manifests enumerateObjectsWithOptions:NSEnumerationReverse usingBlock:^(CKKSManifest* _Nonnull manifest, NSUInteger index, BOOL* _Nonnull stop) {
360 if ([manifest validateWithError:nil]) {
361 result = manifest;
362 *stop = YES;
363 }
364 }];
365
366 // TODO: add error for when we didn't find anything
367 return result;
368 }
369
370 + (SFEC_X962SigningOperation*)signatureOperation
371 {
372 SFECKeySpecifier* keySpecifier = [[SFECKeySpecifier alloc] initWithCurve:SFEllipticCurveNistp384];
373 return [[SFEC_X962SigningOperation alloc] initWithKeySpecifier:keySpecifier digestOperation:[[SFSHA384DigestOperation alloc] init]];
374 }
375
376 + (NSData*)digestForData:(NSData*)data
377 {
378 return [SFSHA384DigestOperation digest:data];
379 }
380
381 + (NSData*)digestValueForLeafRecords:(NSArray*)leafRecords
382 {
383 NSMutableData* concatenatedLeafNodeDigestData = [[NSMutableData alloc] init];
384 for (CKKSManifestLeafRecord* leafRecord in leafRecords) {
385 [concatenatedLeafNodeDigestData appendData:leafRecord.digestValue];
386 }
387
388 return [self digestForData:concatenatedLeafNodeDigestData];
389 }
390
391 + (instancetype)manifestForPendingManifest:(CKKSPendingManifest*)pendingManifest
392 {
393 return [[self alloc] initWithDigestValue:pendingManifest.digestValue zone:pendingManifest.zoneName generationCount:pendingManifest.generationCount leafRecordIDs:pendingManifest.committedLeafRecordIDs peerManifestIDs:pendingManifest.peerManifestIDs currentItems:pendingManifest.currentItems futureData:pendingManifest.futureData signatures:pendingManifest.signatures signerID:pendingManifest.signerID schema:pendingManifest.schema encodedRecord:pendingManifest.encodedCKRecord];
394 }
395
396 + (instancetype)fromDatabaseRow:(NSDictionary*)row
397 {
398 NSString* digestBase64String = row[@"digest"];
399 NSData* digest = [digestBase64String isKindOfClass:[NSString class]] ? [[NSData alloc] initWithBase64EncodedString:digestBase64String options:0] : nil;
400
401 NSString* zone = row[@"ckzone"];
402 NSUInteger generationCount = [row[@"gencount"] integerValue];
403 NSString* signerID = row[@"signerID"];
404
405 NSString* encodedRecordBase64String = row[@"ckrecord"];
406 NSData* encodedRecord = [encodedRecordBase64String isKindOfClass:[NSString class]] ? [[NSData alloc] initWithBase64EncodedString:encodedRecordBase64String options:0] : nil;
407
408 NSData* leafRecordIDData = [[NSData alloc] initWithBase64EncodedString:row[@"leafIDs"] options:0];
409 NSArray* leafRecordIDs = (__bridge_transfer NSArray*)CFPropertyListCreateWithDERData(NULL, (__bridge CFDataRef)leafRecordIDData, 0, NULL, NULL);
410 if (![leafRecordIDs isKindOfClass:[NSArray class]]) {
411 leafRecordIDs = [NSArray array];
412 }
413
414 NSData* peerManifestIDData = [[NSData alloc] initWithBase64EncodedString:row[@"peerManifests"] options:0];
415 NSArray* peerManifestIDs = (__bridge_transfer NSArray*)CFPropertyListCreateWithDERData(NULL, (__bridge CFDataRef)peerManifestIDData, 0, NULL, NULL);
416 if (![peerManifestIDs isKindOfClass:[NSArray class]]) {
417 peerManifestIDs = [NSArray array];
418 }
419
420 NSData* currentItemsData = [[NSData alloc] initWithBase64EncodedString:row[@"currentItems"] options:0];
421 NSDictionary* currentItemsDict = (__bridge_transfer NSDictionary*)CFPropertyListCreateWithDERData(NULL, (__bridge CFDataRef)currentItemsData, 0, NULL, NULL);
422 if (![currentItemsDict isKindOfClass:[NSDictionary class]]) {
423 currentItemsDict = [NSDictionary dictionary];
424 }
425
426 NSData* futureData = [[NSData alloc] initWithBase64EncodedString:row[@"futureData"] options:0];
427 NSDictionary* futureDataDict = (__bridge_transfer NSDictionary*)CFPropertyListCreateWithDERData(NULL, (__bridge CFDataRef)futureData, 0, NULL, NULL);
428 if (![futureDataDict isKindOfClass:[NSDictionary class]]) {
429 futureDataDict = [NSDictionary dictionary];
430 }
431
432 NSData* signaturesData = [[NSData alloc] initWithBase64EncodedString:row[@"signatures"] options:0];
433 NSDictionary* signatures = (__bridge_transfer NSDictionary*)CFPropertyListCreateWithDERData(NULL, (__bridge CFDataRef)signaturesData, 0, NULL, NULL);
434 if (![signatures isKindOfClass:[NSDictionary class]]) {
435 signatures = [NSDictionary dictionary];
436 }
437
438 NSData* schemaData = [[NSData alloc] initWithBase64EncodedString:row[@"schema"] options:0];
439 NSDictionary* schemaDict = (__bridge_transfer NSDictionary*)CFPropertyListCreateWithDERData(NULL, (__bridge CFDataRef)schemaData, 0, NULL, NULL);
440 if (![schemaDict isKindOfClass:[NSDictionary class]]) {
441 schemaDict = __thisBuildsSchema;
442 }
443
444 return [[self alloc] initWithDigestValue:digest zone:zone generationCount:generationCount leafRecordIDs:leafRecordIDs peerManifestIDs:peerManifestIDs currentItems:currentItemsDict futureData:futureDataDict signatures:signatures signerID:signerID schema:schemaDict encodedRecord:encodedRecord];
445 }
446
447 + (NSArray<NSString*>*)sqlColumns
448 {
449 return @[@"ckzone", @"gencount", @"digest", @"signatures", @"signerID", @"leafIDs", @"peerManifests", @"currentItems", @"futureData", @"schema", @"ckrecord"];
450 }
451
452 + (NSString*)sqlTable
453 {
454 return @"ckmanifest";
455 }
456
457 + (NSUInteger)greatestKnownGenerationCount
458 {
459 __block NSUInteger result = 0;
460 [self queryMaxValueForField:@"gencount" inTable:self.sqlTable where:nil columns:@[@"gencount"] processRow:^(NSDictionary* row) {
461 result = [row[@"gencount"] integerValue];
462 }];
463
464 [CKKSPendingManifest queryMaxValueForField:@"gencount" inTable:[CKKSPendingManifest sqlTable] where:nil columns:@[@"gencount"] processRow:^(NSDictionary* row) {
465 result = MAX(result, (NSUInteger)[row[@"gencount"] integerValue]);
466 }];
467
468 return result;
469 }
470
471 - (instancetype)initWithDigestValue:(NSData*)digestValue zone:(NSString*)zone generationCount:(NSUInteger)generationCount leafRecordIDs:(NSArray<NSString*>*)leafRecordIDs peerManifestIDs:(NSArray<NSString*>*)peerManifestIDs currentItems:(NSDictionary*)currentItems futureData:(NSDictionary*)futureData signatures:(NSDictionary*)signatures signerID:(NSString*)signerID schema:(NSDictionary*)schema helper:(CKKSManifestInjectionPointHelper*)helper
472 {
473 if (self = [super init]) {
474 _digestValue = digestValue;
475 _zoneName = zone;
476 _generationCount = generationCount;
477 _leafRecordIDs = [leafRecordIDs copy];
478 _currentItemsDict = currentItems ? [currentItems mutableCopy] : [NSMutableDictionary dictionary];
479 _futureData = futureData ? [futureData copy] : @{};
480 _signaturesDict = [signatures copy];
481 _signerID = signerID;
482 _schema = schema ? schema.copy : __thisBuildsSchema;
483
484 if ([peerManifestIDs.firstObject isEqualToString:signerID]) {
485 _peerManifestIDs = peerManifestIDs;
486 }
487 else {
488 NSMutableArray* tempPeerManifests = [[NSMutableArray alloc] initWithObjects:signerID, nil];
489 if (peerManifestIDs) {
490 [tempPeerManifests addObjectsFromArray:peerManifestIDs];
491 }
492 _peerManifestIDs = tempPeerManifests;
493 }
494
495 _helper = helper ?: [self defaultHelperForSignerID:signerID];
496 if (!_helper) {
497 _helper = [[CKKSManifestInjectionPointHelper alloc] init];
498 }
499 }
500
501 return self;
502 }
503
504 - (instancetype)initWithDigestValue:(NSData*)digestValue zone:(NSString*)zone generationCount:(NSUInteger)generationCount leafRecordIDs:(NSArray<NSString*>*)leafRecordIDs peerManifestIDs:(NSArray<NSString*>*)peerManifestIDs currentItems:(NSDictionary*)currentItems futureData:(NSDictionary*)futureData signatures:(NSDictionary*)signatures signerID:(NSString*)signerID schema:(NSDictionary*)schema
505 {
506 return [self initWithDigestValue:digestValue zone:zone generationCount:generationCount leafRecordIDs:leafRecordIDs peerManifestIDs:peerManifestIDs currentItems:currentItems futureData:futureData signatures:signatures signerID:signerID schema:schema helper:nil];
507 }
508
509 - (instancetype)initWithDigestValue:(NSData*)digestValue zone:(NSString*)zone generationCount:(NSUInteger)generationCount leafRecordIDs:(NSArray<NSString*>*)leafRecordIDs peerManifestIDs:(NSArray<NSString*>*)peerManifestIDs currentItems:(NSDictionary*)currentItems futureData:(NSDictionary*)futureData signatures:(NSDictionary*)signatures signerID:(NSString*)signerID schema:(NSDictionary*)schema encodedRecord:(NSData*)encodedRecord
510 {
511 if (self = [self initWithDigestValue:digestValue zone:zone generationCount:generationCount leafRecordIDs:leafRecordIDs peerManifestIDs:peerManifestIDs currentItems:currentItems futureData:futureData signatures:signatures signerID:signerID schema:schema]) {
512 self.encodedCKRecord = encodedRecord;
513 }
514
515 return self;
516 }
517
518 - (instancetype)initWithCKRecord:(CKRecord*)record
519 {
520 NSError* error = nil;
521 NSString* signatureBase64String = record[SecCKRecordManifestSignaturesKey];
522 if (!signatureBase64String) {
523 ckkserror("ckksmanifest", record.recordID.zoneID, "attempt to create manifest from CKRecord that does not have signatures attached: %@", record);
524 return nil;
525 }
526 NSData* signatureDERData = [[NSData alloc] initWithBase64EncodedString:signatureBase64String options:0];
527 NSDictionary* signaturesDict = [self signatureDictFromDERData:signatureDERData error:&error];
528 if (error) {
529 ckkserror("ckksmanifest", record.recordID.zoneID, "failed to initialize CKKSManifest from CKRecord because we could not form a signature dict from the record: %@", record);
530 return nil;
531 }
532
533 NSDictionary* schemaDict = nil;
534 NSString* schemaBase64String = record[SecCKRecordManifestSchemaKey];
535 if (schemaBase64String) {
536 NSData* schemaData = [[NSData alloc] initWithBase64EncodedString:schemaBase64String options:0];
537 schemaDict = (__bridge_transfer NSDictionary*)CFPropertyListCreateWithDERData(NULL, (__bridge CFDataRef)schemaData, 0, NULL, NULL);
538 }
539 if (![schemaDict isKindOfClass:[NSDictionary class]]) {
540 schemaDict = __thisBuildsSchema;
541 }
542
543 NSString* digestBase64String = record[SecCKRecordManifestDigestValueKey];
544 if (!digestBase64String) {
545 ckkserror("ckksmanifest", record.recordID.zoneID, "attempt to create manifest from CKRecord that does not have a digest attached: %@", record);
546 return nil;
547 }
548 NSData* digestData = [[NSData alloc] initWithBase64EncodedString:digestBase64String options:0];
549
550 if (self = [self initWithDigestValue:digestData
551 zone:record.recordID.zoneID.zoneName
552 generationCount:[record[SecCKRecordManifestGenerationCountKey] unsignedIntegerValue]
553 leafRecordIDs:record[SecCKRecordManifestLeafRecordIDsKey]
554 peerManifestIDs:record[SecCKRecordManifestPeerManifestRecordIDsKey]
555 currentItems:record[SecCKRecordManifestCurrentItemsKey]
556 futureData:[self futureDataDictFromRecord:record withSchema:schemaDict]
557 signatures:signaturesDict
558 signerID:record[SecCKRecordManifestSignerIDKey]
559 schema:schemaDict]) {
560 self.storedCKRecord = record;
561 }
562
563 return self;
564 }
565
566 - (CKKSManifestInjectionPointHelper*)defaultHelperForSignerID:(NSString*)signerID
567 {
568 return __helpersDict[signerID];
569 }
570
571 - (NSDictionary*)signatureDictFromDERData:(NSData*)derData error:(NSError**)error
572 {
573 CFErrorRef localError = NULL;
574 NSDictionary* signaturesDict = (__bridge_transfer NSDictionary*)CFPropertyListCreateWithDERData(NULL, (__bridge CFDataRef)derData, 0, NULL, &localError);
575 if (![signaturesDict isKindOfClass:[NSDictionary class]]) {
576 ckkserror("ckksmanifest", self, "failed to decode signatures der dict with error: %@", localError);
577 if (error) {
578 *error = (__bridge_transfer NSError*)localError;
579 }
580 }
581
582 return signaturesDict;
583 }
584
585 - (NSData*)derDataFromSignatureDict:(NSDictionary*)signatureDict error:(NSError**)error
586 {
587 CFErrorRef localError = NULL;
588 NSData* derData = (__bridge_transfer NSData*)CFPropertyListCreateDERData(NULL, (__bridge CFPropertyListRef)signatureDict, &localError);
589 if (!derData) {
590 ckkserror("ckksmanifest", self, "failed to encode signatures dict to der with error: %@", localError);
591 if (error) {
592 *error = (__bridge_transfer NSError*)localError;
593 }
594 }
595
596 return derData;
597 }
598
599 - (NSArray*)peerManifestsFromDERData:(NSData*)derData error:(NSError**)error
600 {
601 CFErrorRef localError = NULL;
602 NSArray* peerManifests = (__bridge_transfer NSArray*)CFPropertyListCreateWithDERData(NULL, (__bridge CFDataRef)derData, 0, NULL, &localError);
603 if (![peerManifests isKindOfClass:[NSArray class]]) {
604 ckkserror("ckksmanifest", self, "failed to decode peer manifests der array with error: %@", localError);
605 if (error) {
606 *error = (__bridge_transfer NSError*)localError;
607 }
608 }
609
610 return peerManifests;
611 }
612
613 - (NSData*)derDataFromPeerManifests:(NSArray*)peerManifests error:(NSError**)error
614 {
615 CFErrorRef localError = NULL;
616 NSData* derData = (__bridge_transfer NSData*)CFPropertyListCreateDERData(NULL, (__bridge CFPropertyListRef)peerManifests, &localError);
617 if (!derData) {
618 ckkserror("ckksmanifest", self, "failed to encode peer manifests to der with error: %@", localError);
619 if (error) {
620 *error = (__bridge_transfer NSError*)localError;
621 }
622 }
623
624 return derData;
625 }
626
627 - (NSDictionary*)futureDataDictFromRecord:(CKRecord*)record withSchema:(NSDictionary*)cloudSchema
628 {
629 NSMutableDictionary* futureData = [NSMutableDictionary dictionary];
630 [cloudSchema enumerateKeysAndObjectsUsingBlock:^(NSString* key, NSData* obj, BOOL* stop) {
631 if (![__thisBuildsSchema.allKeys containsObject:key]) {
632 futureData[key] = record[key];
633 }
634 }];
635
636 return futureData;
637 }
638
639 - (BOOL)updateWithRecord:(CKRecord*)record error:(NSError**)error
640 {
641 if ([CKKSManifestInjectionPointHelper ignoreChanges]) {
642 return YES; // don't set off any alarms here - just pretend we did it
643 }
644
645 NSData* signatureDERData = [[NSData alloc] initWithBase64EncodedString:record[SecCKRecordManifestSignaturesKey] options:0];
646 NSDictionary* signaturesDict = [self signatureDictFromDERData:signatureDERData error:error];
647 if (!signaturesDict) {
648 return NO;
649 }
650
651 NSData* cloudSchemaData = record[SecCKRecordManifestSchemaKey];
652 NSDictionary* cloudSchemaDict = (__bridge_transfer NSDictionary*)CFPropertyListCreateWithDERData(NULL, (__bridge CFDataRef)cloudSchemaData, 0, NULL, NULL);
653 if (![cloudSchemaDict isKindOfClass:[NSDictionary class]]) {
654 cloudSchemaDict = __thisBuildsSchema;
655 }
656
657 _digestValue = [[NSData alloc] initWithBase64EncodedString:record[SecCKRecordManifestDigestValueKey] options:0];
658 _generationCount = [record[SecCKRecordManifestGenerationCountKey] unsignedIntegerValue];
659 _leafRecordIDs = record[SecCKRecordManifestLeafRecordIDsKey];
660 _peerManifestIDs = record[SecCKRecordManifestPeerManifestRecordIDsKey];
661 _currentItemsDict = [record[SecCKRecordManifestCurrentItemsKey] mutableCopy];
662 if (!_currentItemsDict) {
663 _currentItemsDict = [NSMutableDictionary dictionary];
664 }
665 _futureData = [[self futureDataDictFromRecord:record withSchema:cloudSchemaDict] copy];
666 _signaturesDict = signaturesDict;
667 _signerID = record[SecCKRecordManifestSignerIDKey];
668 _schema = cloudSchemaDict;
669 self.storedCKRecord = record;
670
671 _derData = nil;
672 return YES;
673 }
674
675 - (NSDictionary<NSString*, NSString*>*)sqlValues
676 {
677 void (^addValueSafelyToDictionaryAndLogIfNil)(NSMutableDictionary*, NSString*, id) = ^(NSMutableDictionary* dictionary, NSString* key, id value) {
678 if (!value) {
679 value = [NSNull null];
680 secerror("CKKSManifest: saving manifest to database but %@ is nil", key);
681 }
682
683 dictionary[key] = value;
684 };
685
686 NSMutableDictionary* sqlValues = [[NSMutableDictionary alloc] init];
687 addValueSafelyToDictionaryAndLogIfNil(sqlValues, @"ckzone", _zoneName);
688 addValueSafelyToDictionaryAndLogIfNil(sqlValues, @"gencount", [NSNumber numberWithUnsignedInteger:_generationCount]);
689 addValueSafelyToDictionaryAndLogIfNil(sqlValues, @"digest", self.digestValue);
690 addValueSafelyToDictionaryAndLogIfNil(sqlValues, @"signatures", [[self derDataFromSignatureDict:self.signatures error:nil] base64EncodedStringWithOptions:0]);
691 addValueSafelyToDictionaryAndLogIfNil(sqlValues, @"signerID", _signerID);
692 addValueSafelyToDictionaryAndLogIfNil(sqlValues, @"leafIDs", [(__bridge_transfer NSData*)CFPropertyListCreateDERData(NULL, (__bridge CFPropertyListRef)_leafRecordIDs, NULL) base64EncodedStringWithOptions:0]);
693 addValueSafelyToDictionaryAndLogIfNil(sqlValues, @"peerManifests", [[self derDataFromPeerManifests:_peerManifestIDs error:nil] base64EncodedStringWithOptions:0]);
694 addValueSafelyToDictionaryAndLogIfNil(sqlValues, @"currentItems", [(__bridge_transfer NSData*)CFPropertyListCreateDERData(NULL, (__bridge CFPropertyListRef)_currentItemsDict, NULL) base64EncodedStringWithOptions:0]);
695 addValueSafelyToDictionaryAndLogIfNil(sqlValues, @"futureData", [(__bridge_transfer NSData*)CFPropertyListCreateDERData(NULL, (__bridge CFPropertyListRef)_futureData, NULL) base64EncodedStringWithOptions:0]);
696 addValueSafelyToDictionaryAndLogIfNil(sqlValues, @"schema", [(__bridge_transfer NSData*)CFPropertyListCreateDERData(NULL, (__bridge CFPropertyListRef)_schema, NULL) base64EncodedStringWithOptions:0]);
697 addValueSafelyToDictionaryAndLogIfNil(sqlValues, @"ckrecord", [self.encodedCKRecord base64EncodedStringWithOptions:0]);
698
699 return sqlValues;
700 }
701
702 - (NSDictionary<NSString*, id>*)whereClauseToFindSelf
703 {
704 return @{ @"ckzone" : CKKSNilToNSNull(_zoneName),
705 @"gencount" : [NSNumber numberWithUnsignedInteger:_generationCount],
706 @"signerID" : CKKSNilToNSNull(_signerID) };
707 }
708
709 - (NSString*)CKRecordName
710 {
711 return [NSString stringWithFormat:@"Manifest:-:%@:-:%@:-:%lu", _zoneName, _signerID, (unsigned long)_generationCount];
712 }
713
714 + (NSDictionary*)whereClauseForRecordName:(NSString*)recordName
715 {
716 NSArray* components = [recordName componentsSeparatedByString:@":-:"];
717 if (components.count < 4) {
718 secerror("CKKSManifest: could not parse components from record name: %@", recordName);
719 }
720
721 return @{ @"ckzone" : components[1],
722 @"signerID" : components[2],
723 @"gencount" : components[3] };
724 }
725
726 - (CKRecord*)updateCKRecord:(CKRecord*)record zoneID:(CKRecordZoneID*)zoneID
727 {
728 if (![record.recordType isEqualToString:SecCKRecordManifestType]) {
729 @throw [NSException exceptionWithName:@"WrongCKRecordTypeException" reason:[NSString stringWithFormat:@"CKRecorType (%@) was not %@", record.recordType, SecCKRecordManifestType] userInfo:nil];
730 }
731
732 NSData* signatureDERData = [self derDataFromSignatureDict:self.signatures error:nil];
733 if (!signatureDERData) {
734 return record;
735 }
736
737 record[SecCKRecordManifestDigestValueKey] = [self.digestValue base64EncodedStringWithOptions:0];
738 record[SecCKRecordManifestGenerationCountKey] = [NSNumber numberWithUnsignedInteger:_generationCount];
739 record[SecCKRecordManifestLeafRecordIDsKey] = _leafRecordIDs;
740 record[SecCKRecordManifestPeerManifestRecordIDsKey] = _peerManifestIDs;
741 record[SecCKRecordManifestCurrentItemsKey] = [(__bridge_transfer NSData*)CFPropertyListCreateDERData(NULL, (__bridge CFDictionaryRef)_currentItemsDict, NULL) base64EncodedStringWithOptions:0];
742 record[SecCKRecordManifestSignaturesKey] = [signatureDERData base64EncodedStringWithOptions:0];
743 record[SecCKRecordManifestSignerIDKey] = _signerID;
744 record[SecCKRecordManifestSchemaKey] = [(__bridge_transfer NSData*)CFPropertyListCreateDERData(NULL, (__bridge CFDictionaryRef)_schema, NULL) base64EncodedStringWithOptions:0];
745
746 [_futureData enumerateKeysAndObjectsUsingBlock:^(NSString* key, id futureField, BOOL* stop) {
747 record[key] = futureField;
748 }];
749
750 return record;
751 }
752
753 - (bool)matchesCKRecord:(CKRecord*)record
754 {
755 if (![record.recordType isEqualToString:SecCKRecordManifestType]) {
756 return false;
757 }
758
759 NSError* error = nil;
760 NSData* signatureDERData = [[NSData alloc] initWithBase64EncodedString:record[SecCKRecordManifestSignaturesKey] options:0];
761 NSDictionary* signaturesDict = [self signatureDictFromDERData:signatureDERData error:&error];
762 if (!signaturesDict || error) {
763 return NO;
764 }
765
766 NSData* digestData = [[NSData alloc] initWithBase64EncodedString:record[SecCKRecordManifestDigestValueKey] options:0];
767 return [digestData isEqual:self.digestValue] &&
768 [record[SecCKRecordManifestGenerationCountKey] unsignedIntegerValue] == _generationCount &&
769 [record[SecCKRecordManifestPeerManifestRecordIDsKey] isEqual:_peerManifestIDs] &&
770 [signaturesDict isEqual:self.signatures] &&
771 [record[SecCKRecordManifestSignerIDKey] isEqual:_signerID];
772 }
773
774 - (void)setFromCKRecord:(CKRecord*)record
775 {
776 NSError* error = nil;
777 if (![self updateWithRecord:record error:&error]) {
778 ckkserror("ckksmanifest", self, "failed to update manifest from CKRecord with error: %@", error);
779 }
780 }
781
782 - (NSData*)derData
783 {
784 if (!_derData) {
785 NSError* error = nil;
786 _derData = ManifestDERData(_zoneName, self.digestValue, _peerManifestIDs, _currentItemsDict, _generationCount, _futureData, _schema, &error);
787 if (error) {
788 ckkserror("ckksmanifest", self, "error encoding manifest into DER: %@", error);
789 _derData = nil;
790 }
791 }
792
793 return _derData;
794 }
795
796 - (BOOL)validateWithError:(NSError**)error
797 {
798 __block BOOL verified = false;
799 NSData* manifestDerData = self.derData;
800 if (manifestDerData) {
801 __block NSError* localError = nil;
802
803 [_helper performWithPeerVerifyingKeys:^(NSDictionary<NSString*, SFECPublicKey*>* _Nullable peerKeys, NSError* _Nullable error) {
804 if(error) {
805 ckkserror("ckksmanifest", self, "Error fetching peer verifying keys: %@", error);
806 }
807 SFECPublicKey* verifyingKey = peerKeys[self->_signerID];
808 if (verifyingKey) {
809 SFEC_X962SigningOperation* signingOperation = [self.class signatureOperation];
810 SFSignedData* signedData = [[SFSignedData alloc] initWithData:manifestDerData signature:self.signatures[CKKSManifestEC384SignatureKey]];
811 verified = [signingOperation verify:signedData withKey:verifyingKey error:&localError] == NULL ? false : true;
812
813 }
814 else {
815 localError = [NSError errorWithDomain:CKKSManifestErrorDomain
816 code:CKKSManifestErrorVerifyingKeyNotFound
817 userInfo:@{NSLocalizedDescriptionKey : [NSString localizedStringWithFormat:@"could not find manifest public key for peer %@", self->_signerID],
818 NSUnderlyingErrorKey: CKKSNilToNSNull(error)}];
819 }
820 }];
821
822 if (error) {
823 *error = localError;
824 }
825 }
826
827 return verified;
828 }
829
830 - (BOOL)validateItem:(CKKSItem*)item withError:(NSError**)error
831 {
832 NSString* uuid = item.uuid;
833 CKKSManifestLeafRecord* leafRecord = [self leafRecordForItemUUID:uuid];
834 NSData* expectedItemDigest = leafRecord.recordDigestDict[uuid];
835 if ([[self.class digestForData:item.encitem] isEqual:expectedItemDigest]) {
836 return YES;
837 }
838 else if (error) {
839 *error = [NSError errorWithDomain:CKKSManifestErrorDomain code:CKKSManifestErrorInvalidDigest userInfo:@{NSLocalizedDescriptionKey : @"could not validate item because the digest is invalid"}];
840 }
841
842 return NO;
843 }
844
845 - (BOOL)validateCurrentItem:(CKKSCurrentItemPointer*)currentItem withError:(NSError**)error
846 {
847 BOOL result = [currentItem.currentItemUUID isEqualToString:[_currentItemsDict valueForKey:currentItem.identifier]];
848 if (!result && error) {
849 *error = [NSError errorWithDomain:CKKSManifestErrorDomain code:CKKSManifestErrorCurrentItemUUIDNotFound userInfo:@{NSLocalizedDescriptionKey :@"could not validate current item because the UUID does not match the manifest"}];
850 }
851
852 return result;
853 }
854
855 - (BOOL)itemUUIDExistsInManifest:(NSString*)uuid
856 {
857 CKKSManifestLeafRecord* leafRecord = [self leafRecordForItemUUID:uuid];
858 return leafRecord.recordDigestDict[uuid] != nil;
859 }
860
861 - (BOOL)contentsAreEqualToManifest:(CKKSManifest*)otherManifest
862 {
863 return [_digestValue isEqual:otherManifest.digestValue];
864 }
865
866 - (CKKSManifestLeafRecord*)leafRecordForID:(NSString*)leafRecordID
867 {
868 NSError* error = nil;
869 CKKSManifestLeafRecord* leafRecord = [CKKSManifestLeafRecord leafRecordForID:leafRecordID error:&error];
870 if (error || !leafRecord) {
871 ckkserror("ckksmanifest", self, "failed to lookup manifest leaf record with id: %@ error: %@", leafRecordID, error);
872 }
873
874 return leafRecord;
875 }
876
877 - (CKKSManifestLeafRecord*)leafRecordForItemUUID:(NSString*)uuid
878 {
879 NSInteger bucketIndex = LeafBucketIndexForUUID(uuid);
880 NSString* leafRecordID = _leafRecordIDs[bucketIndex];
881 return [self leafRecordForID:leafRecordID];
882 }
883
884 - (void)clearDigest
885 {
886 _digestValue = nil;
887 _derData = nil;
888 _signaturesDict = nil;
889 }
890
891 - (NSData*)digestValue
892 {
893 if (!_digestValue) {
894 _digestValue = [self.class digestValueForLeafRecords:self.leafRecords];
895 }
896
897 return _digestValue;
898 }
899
900 - (NSArray<CKKSManifestLeafRecord*>*)leafRecords
901 {
902 NSMutableArray* leafRecords = [[NSMutableArray alloc] initWithCapacity:_leafRecordIDs.count];
903 for (NSString* recordID in _leafRecordIDs) {
904 CKKSManifestLeafRecord* leafRecord = [self leafRecordForID:recordID];
905 if(leafRecord) {
906 [leafRecords addObject:leafRecord];
907 } else {
908 ckkserror("ckksmanifest", self, "failed to fetch leaf record from CKManifest for %@", recordID);
909 // TODO: auto bug capture?
910 }
911 }
912
913 return leafRecords;
914 }
915
916 - (NSString*)ckRecordType
917 {
918 return SecCKRecordManifestType;
919 }
920
921 - (void)nilAllIvars
922 {
923 _derData = nil;
924 _digestValue = nil;
925 _signerID = nil;
926 _zoneName = nil;
927 _leafRecordIDs = nil;
928 _peerManifestIDs = nil;
929 _currentItemsDict = nil;
930 _futureData = nil;
931 _signaturesDict = nil;
932 _schema = nil;
933 }
934
935 @end
936
937 @implementation CKKSPendingManifest
938
939 @synthesize committedLeafRecordIDs = _committedLeafRecordIDs;
940
941 + (NSString*)sqlTable
942 {
943 return @"pending_manifest";
944 }
945
946 - (BOOL)isReadyToCommit
947 {
948 for (NSString* leafRecordID in self.leafRecordIDs) {
949 if ([CKKSManifestLeafRecord recordExistsForID:leafRecordID] || [CKKSManifestPendingLeafRecord recordExistsForID:leafRecordID]) {
950 continue;
951 }
952 else {
953 ckksinfo("ckksmanifest", self, "Not ready to commit manifest, yet - missing leaf record ID: %@", leafRecordID);
954 return NO;
955 }
956 }
957
958 return YES;
959 }
960
961 - (CKKSManifest*)commitToDatabaseWithError:(NSError**)error
962 {
963 NSError* localError = nil;
964
965 _committedLeafRecordIDs = [[NSMutableArray alloc] init];
966
967 for (NSString* leafRecordID in self.leafRecordIDs) {
968 CKKSManifestPendingLeafRecord* pendingLeaf = [CKKSManifestPendingLeafRecord leafRecordForID:leafRecordID error:&localError];
969 if (pendingLeaf) {
970 CKKSManifestLeafRecord* committedLeaf = [pendingLeaf commitToDatabaseWithError:error];
971 if (committedLeaf) {
972 [_committedLeafRecordIDs addObject:committedLeaf.CKRecordName];
973 }
974 else {
975 return nil;
976 }
977 }
978 else {
979 CKKSManifestLeafRecord* existingLeaf = [CKKSManifestLeafRecord leafRecordForID:leafRecordID error:&localError];
980 if (existingLeaf) {
981 [_committedLeafRecordIDs addObject:existingLeaf.CKRecordName];
982 continue;
983 }
984 }
985
986 if (localError) {
987 if (error) {
988 *error = localError;
989 }
990 return nil;
991 }
992 }
993
994 CKKSManifest* manifest = [CKKSManifest manifestForPendingManifest:self];
995 if ([manifest saveToDatabase:error]) {
996 [self deleteFromDatabase:error];
997 return manifest;
998 }
999 else {
1000 return nil;
1001 }
1002 }
1003
1004 @end
1005
1006 @implementation CKKSEgoManifest
1007
1008 + (CKKSManifestInjectionPointHelper*)egoHelper
1009 {
1010 return __egoHelper ?: [[CKKSManifestInjectionPointHelper alloc] init];
1011 }
1012
1013 + (NSArray*)leafRecordsForItems:(NSArray*)items manifestName:(NSString*)manifestName zone:(NSString*)zone
1014 {
1015 NSMutableArray* leafRecords = [[NSMutableArray alloc] init];
1016 for (NSInteger i = 0; i < NUM_MANIFEST_LEAF_RECORDS; i++) {
1017 [leafRecords addObject:[CKKSEgoManifestLeafRecord newLeafRecordInZone:zone]];
1018 }
1019
1020 for (CKKSItem* item in items) {
1021 CKKSEgoManifestLeafRecord* leafRecord = leafRecords[LeafBucketIndexForUUID(item.uuid)];
1022 [leafRecord addOrUpdateRecordUUID:item.uuid withEncryptedItemData:item.encitem];
1023 }
1024
1025 return leafRecords;
1026 }
1027
1028 + (nullable CKKSEgoManifest*)tryCurrentEgoManifestForZone:(NSString*)zone
1029 {
1030 __block CKKSEgoManifest* manifest = nil;
1031 [self.egoHelper performWithEgoPeerID:^(NSString * _Nullable egoPeerID, NSError * _Nullable error) {
1032 if(error) {
1033 ckkserrorwithzonename("ckksmanifest", zone, "Error getting peer ID: %@", error);
1034 return;
1035 }
1036 if (!egoPeerID) {
1037 ckkserrorwithzonename("ckksmanifest", zone, "can't get ego peer ID right now - the device probably hasn't been unlocked yet");
1038 return;
1039 }
1040
1041 NSDictionary* whereDict = @{ @"ckzone" : zone, @"signerID" : egoPeerID };
1042 [self queryMaxValueForField:@"gencount" inTable:self.sqlTable where:whereDict columns:self.sqlColumns processRow:^(NSDictionary* row) {
1043 manifest = [self fromDatabaseRow:row];
1044 }];
1045 }];
1046
1047 return manifest;
1048 }
1049
1050 + (nullable instancetype)newFakeManifestForZone:(NSString*)zone withItemRecords:(NSArray<CKRecord*>*)itemRecords currentItems:(NSDictionary*)currentItems signerID:(NSString*)signerID keyPair:(SFECKeyPair*)keyPair error:(NSError**)error
1051 {
1052 CKKSManifestInjectionPointHelper* helper = [[CKKSManifestInjectionPointHelper alloc] initWithPeerID:signerID keyPair:keyPair isEgoPeer:NO];
1053 CKKSEgoManifest* manifest = [self newManifestForZone:zone withItems:@[] peerManifestIDs:@[] currentItems:currentItems error:error helper:helper];
1054 manifest.signerID = signerID;
1055 manifest.helper = helper;
1056 [manifest updateWithNewOrChangedRecords:itemRecords deletedRecordIDs:@[]];
1057 return manifest;
1058 }
1059
1060 + (nullable instancetype)newManifestForZone:(NSString*)zone withItems:(NSArray<CKKSItem*>*)items peerManifestIDs:(NSArray<NSString*>*)peerManifestIDs currentItems:(NSDictionary*)currentItems error:(NSError**)error
1061 {
1062 return [self newManifestForZone:zone withItems:items peerManifestIDs:peerManifestIDs currentItems:currentItems error:error helper:self.egoHelper];
1063 }
1064
1065 + (nullable instancetype)newManifestForZone:(NSString*)zone withItems:(NSArray<CKKSItem*>*)items peerManifestIDs:(NSArray<NSString*>*)peerManifestIDs currentItems:(NSDictionary*)currentItems error:(NSError**)error helper:(CKKSManifestInjectionPointHelper*)helper
1066 {
1067 __block NSError* localError = nil;
1068 NSArray* leafRecords = [self leafRecordsForItems:items manifestName:nil zone:zone];
1069 NSData* digestValue = [self digestValueForLeafRecords:leafRecords];
1070
1071 NSInteger generationCount = [self greatestKnownGenerationCount] + 1;
1072
1073 __block CKKSEgoManifest* result = nil;
1074 [helper performWithEgoPeerID:^(NSString* _Nullable egoPeerID, NSError* _Nullable err) {
1075 if (err) {
1076 localError = err;
1077 }
1078 else if (egoPeerID) {
1079 result = [[self alloc] initWithDigestValue:digestValue zone:zone generationCount:generationCount leafRecords:leafRecords peerManifestIDs:peerManifestIDs currentItems:currentItems futureData:[NSDictionary dictionary] signatures:nil signerID:egoPeerID schema:__thisBuildsSchema];
1080 }
1081 else {
1082 localError = [NSError errorWithDomain:CKKSManifestErrorDomain code:CKKSManifestErrorManifestGenerationFailed userInfo:@{NSLocalizedDescriptionKey : @"failed to generate ego manifest because egoPeerID is nil"}];
1083 }
1084 }];
1085
1086 if (!result && !localError) {
1087 localError = [NSError errorWithDomain:CKKSManifestErrorDomain code:CKKSManifestErrorManifestGenerationFailed userInfo:@{NSLocalizedDescriptionKey : @"failed to generate ego manifest"}];
1088 }
1089 if (error) {
1090 *error = localError;
1091 }
1092
1093 return result;
1094 }
1095
1096 + (instancetype)fromDatabaseWhere:(NSDictionary *)whereDict error:(NSError * __autoreleasing *)error {
1097 CKKSEgoManifest* manifest = [super fromDatabaseWhere:whereDict error:error];
1098 if(!manifest) {
1099 return nil;
1100 }
1101
1102 // Try to load leaf records
1103 if(![manifest loadLeafRecords:manifest.zoneID.zoneName error:error]) {
1104 return nil;
1105 }
1106
1107 return manifest;
1108 }
1109
1110 + (instancetype)tryFromDatabaseWhere:(NSDictionary *)whereDict error:(NSError * __autoreleasing *)error {
1111 CKKSEgoManifest* manifest = [super fromDatabaseWhere:whereDict error:error];
1112 if(!manifest) {
1113 return nil;
1114 }
1115
1116 // Try to load leaf records
1117 // Failing to load leaf records on a manifest that exists is an error, even in tryFromDatabaseWhere.
1118 if(![manifest loadLeafRecords:manifest.zoneID.zoneName error:error]) {
1119 return nil;
1120 }
1121
1122 return manifest;
1123 }
1124
1125 - (bool)loadLeafRecords:(NSString*)ckzone error:(NSError * __autoreleasing *)error {
1126 NSMutableArray* leafRecords = [[NSMutableArray alloc] initWithCapacity:self.leafRecordIDs.count];
1127 for (NSString* leafID in self.leafRecordIDs) {
1128 CKKSEgoManifestLeafRecord* leafRecord = [CKKSEgoManifestLeafRecord fromDatabaseWhere:@{@"uuid" : [CKKSManifestLeafRecord leafUUIDForRecordID:leafID], @"ckzone" : ckzone} error:error];
1129 if (leafRecord) {
1130 [leafRecords addObject:leafRecord];
1131 } else {
1132 secerror("ckksmanifest: error loading leaf record from database: %@", error ? *error : nil);
1133 return false;
1134 }
1135 }
1136
1137 self->_leafRecords = leafRecords;
1138 return true;
1139 }
1140
1141 + (NSDictionary*)generateSignaturesWithHelper:(CKKSManifestInjectionPointHelper*)helper derData:(NSData*)manifestDerData error:(NSError**)error
1142 {
1143 __block NSData* signature = nil;
1144 __block NSError* localError = nil;
1145 [helper performWithSigningKey:^(SFECKeyPair* _Nullable signingKey, NSError* _Nullable err) {
1146 if (err) {
1147 localError = err;
1148 return;
1149 }
1150
1151 if (signingKey) {
1152 SFEC_X962SigningOperation* signingOperation = [self signatureOperation];
1153 SFSignedData* signedData = [signingOperation sign:manifestDerData withKey:signingKey error:&localError];
1154 signature = signedData.signature;
1155 }
1156 }];
1157
1158 if(error) {
1159 *error = localError;
1160 }
1161
1162 return signature ? @{CKKSManifestEC384SignatureKey : signature} : nil;
1163 }
1164
1165 - (instancetype)initWithDigestValue:(NSData*)digestValue zone:(NSString*)zone generationCount:(NSUInteger)generationCount leafRecords:(NSArray<CKKSManifestLeafRecord*>*)leafRecords peerManifestIDs:(NSArray<NSString*>*)peerManifestIDs currentItems:(NSDictionary*)currentItems futureData:(NSDictionary*)futureData signatures:(NSDictionary*)signatures signerID:(NSString*)signerID schema:(NSDictionary*)schema
1166 {
1167 NSMutableArray* leafRecordIDs = [[NSMutableArray alloc] initWithCapacity:leafRecords.count];
1168 for (CKKSManifestLeafRecord* leafRecord in leafRecords) {
1169 [leafRecordIDs addObject:leafRecord.CKRecordName];
1170 }
1171
1172 if (self = [super initWithDigestValue:digestValue zone:zone generationCount:generationCount leafRecordIDs:leafRecordIDs peerManifestIDs:peerManifestIDs currentItems:currentItems futureData:futureData signatures:signatures signerID:signerID schema:schema helper:[CKKSEgoManifest egoHelper]]) {
1173 _leafRecords = leafRecords.copy;
1174 }
1175
1176 return self;
1177 }
1178
1179 - (void)updateWithNewOrChangedRecords:(NSArray<CKRecord*>*)newOrChangedRecords deletedRecordIDs:(NSArray<CKRecordID*>*)deletedRecordIDs
1180 {
1181 if ([CKKSManifestInjectionPointHelper ignoreChanges]) {
1182 return;
1183 }
1184
1185 for (CKRecordID* deletedRecord in deletedRecordIDs) {
1186 NSString* deletedUUID = deletedRecord.recordName;
1187 CKKSEgoManifestLeafRecord* leafRecord = [self leafRecordForItemUUID:deletedUUID];
1188 [leafRecord deleteItemWithUUID:deletedUUID];
1189 }
1190
1191 for (CKRecord* record in newOrChangedRecords) {
1192 CKKSEgoManifestLeafRecord* leafRecord = (CKKSEgoManifestLeafRecord*)[self leafRecordForItemUUID:record.recordID.recordName];
1193 [leafRecord addOrUpdateRecord:record];
1194 }
1195
1196 [self clearDigest];
1197 _generationCount = [self.class greatestKnownGenerationCount] + 1;
1198 }
1199
1200 - (void)setCurrentItemUUID:(NSString*)newCurrentItemUUID forIdentifier:(NSString*)currentPointerIdentifier
1201 {
1202 _currentItemsDict[currentPointerIdentifier] = newCurrentItemUUID;
1203 [self clearDigest];
1204 _generationCount = [self.class greatestKnownGenerationCount] + 1;
1205 }
1206
1207 - (CKKSEgoManifestLeafRecord*)leafRecordForItemUUID:(NSString*)uuid
1208 {
1209 NSUInteger leafBucket = LeafBucketIndexForUUID(uuid);
1210 if(_leafRecords.count > leafBucket) {
1211 return _leafRecords[leafBucket];
1212 } else {
1213 return nil;
1214 }
1215 }
1216
1217 - (NSArray<CKKSManifestLeafRecord*>*)leafRecords
1218 {
1219 return _leafRecords;
1220 }
1221
1222 - (NSArray<CKRecord*>*)allCKRecordsWithZoneID:(CKRecordZoneID*)zoneID
1223 {
1224 NSMutableArray* records = [[NSMutableArray alloc] initWithCapacity:_leafRecords.count + 1];
1225 [records addObject:[self CKRecordWithZoneID:zoneID]];
1226
1227 for (CKKSManifestLeafRecord* leafRecord in _leafRecords) {
1228 [records addObject:[leafRecord CKRecordWithZoneID:zoneID]];
1229 }
1230
1231 return records;
1232 }
1233
1234 - (bool)saveToDatabase:(NSError**)error
1235 {
1236 bool result = [super saveToDatabase:error];
1237 if (result) {
1238 for (CKKSManifestLeafRecord* leafRecord in _leafRecords) {
1239 result &= [leafRecord saveToDatabase:error];
1240 }
1241 }
1242
1243 return result;
1244 }
1245
1246 - (NSDictionary*)signatures
1247 {
1248 if (!_signaturesDict) {
1249 _signaturesDict = [self.class generateSignaturesWithHelper:self.helper derData:self.derData error:nil];
1250 }
1251
1252 return _signaturesDict;
1253 }
1254
1255 - (void)setSignatures:(NSDictionary*)signatures
1256 {
1257 _signaturesDict = signatures;
1258 }
1259
1260 - (CKKSManifestInjectionPointHelper*)defaultHelperForSignerID:(NSString*)signerID
1261 {
1262 CKKSManifestInjectionPointHelper* helper = __helpersDict[signerID];
1263 return helper ?: __egoHelper;
1264 }
1265
1266 @end
1267
1268 @implementation CKKSManifestInjectionPointHelper {
1269 NSString* _peerID;
1270 SFECKeyPair* _keyPair;
1271 }
1272
1273 + (void)registerHelper:(CKKSManifestInjectionPointHelper*)helper forPeer:(NSString*)peerID
1274 {
1275 if (!__helpersDict) {
1276 __helpersDict = [[NSMutableDictionary alloc] init];
1277 }
1278
1279 __helpersDict[peerID] = helper;
1280 }
1281
1282 + (void)registerEgoPeerID:(NSString*)egoPeerID keyPair:(SFECKeyPair*)keyPair
1283 {
1284 __egoHelper = [[self alloc] initWithPeerID:egoPeerID keyPair:keyPair isEgoPeer:YES];
1285 }
1286
1287 + (BOOL)ignoreChanges
1288 {
1289 return __ignoreChanges;
1290 }
1291
1292 + (void)setIgnoreChanges:(BOOL)ignoreChanges
1293 {
1294 __ignoreChanges = ignoreChanges ? YES : NO;
1295 }
1296
1297 - (instancetype)initWithPeerID:(NSString*)peerID keyPair:(SFECKeyPair*)keyPair isEgoPeer:(BOOL)isEgoPeer
1298 {
1299 if (self = [super init]) {
1300 _peerID = peerID;
1301 _keyPair = keyPair;
1302 if (isEgoPeer) {
1303 __egoHelper = self;
1304 }
1305 else {
1306 [self.class registerHelper:self forPeer:peerID];
1307 }
1308 }
1309
1310 return self;
1311 }
1312
1313 - (NSString*)description
1314 {
1315 return [NSString stringWithFormat:@"%@ peerID: (%@)", [super description], _peerID];
1316 }
1317
1318 - (void)performWithSigningKey:(void (^)(SFECKeyPair* _Nullable signingKey, NSError* _Nullable error))handler
1319 {
1320 if (s_accountInfo) {
1321 if(s_accountInfo.setupError) {
1322 handler(nil, s_accountInfo.setupError);
1323 } else {
1324 handler(s_accountInfo.signingKey, nil);
1325 }
1326 }
1327 else if (_keyPair) {
1328 handler(_keyPair, nil);
1329 }
1330 else {
1331 SOSCCPerformWithOctagonSigningKey(^(SecKeyRef signingSecKey, CFErrorRef err) {
1332 SFECKeyPair* key = nil;
1333 if (!err && signingSecKey) {
1334 key = [[SFECKeyPair alloc] initWithSecKey:signingSecKey];
1335 }
1336
1337 handler(key, (__bridge NSError*)err);
1338 });
1339 }
1340 }
1341
1342 - (void)performWithEgoPeerID:(void (^)(NSString* _Nullable egoPeerID, NSError* _Nullable error))handler
1343 {
1344 if (s_accountInfo) {
1345 if(s_accountInfo.setupError) {
1346 handler(nil, s_accountInfo.setupError);
1347 } else {
1348 handler(s_accountInfo.egoPeerID, nil);
1349 }
1350 }
1351 else if (_peerID) {
1352 handler(_peerID, nil);
1353 }
1354 else {
1355 NSError* error = nil;
1356 SOSPeerInfoRef egoPeerInfo = SOSCCCopyMyPeerInfo(NULL);
1357 NSString* egoPeerID = egoPeerInfo ? (__bridge NSString*)SOSPeerInfoGetPeerID(egoPeerInfo) : nil;
1358 handler(egoPeerID, error);
1359 CFReleaseNull(egoPeerInfo);
1360 }
1361 }
1362
1363 - (void)performWithPeerVerifyingKeys:(void (^)(NSDictionary<NSString*, SFECPublicKey*>* _Nullable peerKeys, NSError* _Nullable error))handler
1364 {
1365 if (s_accountInfo) {
1366 if(s_accountInfo.setupError) {
1367 handler(nil, s_accountInfo.setupError);
1368 } else {
1369 handler(s_accountInfo.peerVerifyingKeys, nil);
1370 }
1371 }
1372 else if (__egoHelper || __helpersDict) {
1373 NSMutableDictionary* verifyingKeys = [[NSMutableDictionary alloc] init];
1374 [__helpersDict enumerateKeysAndObjectsUsingBlock:^(NSString* _Nonnull peer, CKKSManifestInjectionPointHelper* _Nonnull helper, BOOL* _Nonnull stop) {
1375 verifyingKeys[peer] = helper.keyPair.publicKey;
1376 }];
1377 if (__egoHelper.keyPair) {
1378 verifyingKeys[__egoHelper.peerID] = __egoHelper.keyPair.publicKey;
1379 }
1380 handler(verifyingKeys, nil);
1381 }
1382 else {
1383 CFErrorRef error = NULL;
1384 NSMutableDictionary* peerKeys = [NSMutableDictionary dictionary];
1385 CFArrayRef peerInfos = SOSCCCopyValidPeerPeerInfo(&error);
1386 if (!peerInfos || error) {
1387 handler(nil, (__bridge NSError*)error);
1388 CFReleaseNull(peerInfos);
1389 CFReleaseNull(error);
1390 return;
1391 }
1392
1393 CFArrayForEach(peerInfos, ^(const void* peerInfoPtr) {
1394 SOSPeerInfoRef peerInfo = (SOSPeerInfoRef)peerInfoPtr;
1395 CFErrorRef blockError = NULL;
1396 SecKeyRef secPublicKey = SOSPeerInfoCopyOctagonSigningPublicKey(peerInfo, &blockError);
1397 if (!secPublicKey || blockError) {
1398 CFReleaseNull(secPublicKey);
1399 CFReleaseNull(blockError);
1400 return;
1401 }
1402
1403 SFECPublicKey* publicKey = [[SFECPublicKey alloc] initWithSecKey:secPublicKey];
1404 CFReleaseNull(secPublicKey);
1405 NSString* peerID = (__bridge NSString*)SOSPeerInfoGetPeerID(peerInfo);
1406 peerKeys[peerID] = publicKey;
1407 });
1408
1409 handler(peerKeys, nil);
1410 CFReleaseNull(peerInfos);
1411 }
1412 }
1413
1414 - (SFECKeyPair*)keyPair
1415 {
1416 return _keyPair;
1417 }
1418
1419 - (NSString*)peerID
1420 {
1421 return _peerID;
1422 }
1423
1424 @end
1425
1426 @implementation CKKSAccountInfo
1427
1428 @synthesize signingKey = _signingKey;
1429 @synthesize peerVerifyingKeys = _peerVerifyingKeys;
1430 @synthesize egoPeerID = _egoPeerID;
1431
1432 @end
1433
1434 #endif