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