]> git.saurik.com Git - apple/security.git/blame - OSX/sec/SOSCircle/SecureObjectSync/SOSDigestVector.c
Security-57740.1.18.tar.gz
[apple/security.git] / OSX / sec / SOSCircle / SecureObjectSync / SOSDigestVector.c
CommitLineData
d8f41ccd 1/*
5c19dc3a 2 * Copyright (c) 2013-2015 Apple Inc. All Rights Reserved.
d8f41ccd
A
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
5c19dc3a 25#include <Security/SecureObjectSync/SOSDigestVector.h>
d8f41ccd
A
26#include <utilities/SecCFError.h>
27#include <utilities/SecCFWrappers.h>
28#include <utilities/comparison.h>
29#include <dispatch/dispatch.h>
30#include <stdlib.h>
31
32CFStringRef kSOSDigestVectorErrorDomain = CFSTR("com.apple.security.sos.digestvector.error");
33
34/* SOSDigestVector code. */
35
fa7225c8
A
36const size_t kMaxDVCapacity = (1024*1024L); // roughly based on KVS limit, helps avoid integer overflow issues
37
38static bool SOSDigestVectorEnsureCapacity(struct SOSDigestVector *dv, size_t count) {
39 // Note that capacity is the number of digests it can hold, not the size in bytes
40 // Already big enough.
41 if (count <= dv->capacity)
42 return true;
43 // Too big
44 if (count > kMaxDVCapacity) {
45 secerror("Requesting too much space for digest vectors: %ld", count);
46 return false;
47 }
d8f41ccd 48
fa7225c8
A
49 size_t capacity = (dv->capacity + 16) * 3 / 2;
50 size_t digestSize = sizeof(*(dv->digest));
51 if (capacity < count)
52 capacity = count;
53 dv->digest = reallocf(dv->digest, digestSize * capacity);
54 if (dv->digest == NULL) {
55 dv->count = 0;
56 secerror("reallocf failed requesting space for digest vectors: %ld (bytes)", digestSize * capacity);
57 return false;
58 }
59 dv->capacity = capacity;
60 return true;
d8f41ccd
A
61}
62
63static void SOSDigestVectorAppendOrdered(struct SOSDigestVector *dv, const uint8_t *digest)
64{
fa7225c8
A
65 if (SOSDigestVectorEnsureCapacity(dv, dv->count + 1))
66 memcpy(dv->digest[dv->count++], digest, SOSDigestSize);
d8f41ccd
A
67}
68
69void SOSDigestVectorAppend(struct SOSDigestVector *dv, const uint8_t *digest)
70{
71 SOSDigestVectorAppendOrdered(dv, digest);
72 dv->unsorted = true;
73}
74
75static int SOSDigestCompare(const void *a, const void *b)
76{
77 return memcmp(a, b, SOSDigestSize);
78}
79
fa7225c8
A
80// Remove duplicates from sorted manifest using minimal memmove() calls
81static void SOSDigestVectorUnique(struct SOSDigestVector *dv) {
82 if (dv->count < 2 || dv->digest == NULL)
5c19dc3a
A
83 return;
84
85 const uint8_t *prev = dv->digest[0];
86 uint8_t *dest = dv->digest[1];
87 const uint8_t *end = dv->digest[dv->count];
88 const uint8_t *source = dest;
89 for (const uint8_t *cur = source; cur < end; cur += SOSDigestSize) {
90 int delta = SOSDigestCompare(prev, cur);
91 if (delta < 0) {
92 // Found a properly sorted element
93 // 1) Extend the current region (prev is end of region pointer)
94 // 2) Reset prev
95 prev = cur;
96 } else if (delta > 0) {
97 // DigestVector wasn't sorted!
98 assert(delta <= 0);
99 } else {
100 // Found a duplicate element
101 // 1) Finish copy for current region up to previous element
102 prev += SOSDigestSize;
103 if (dest != source)
fa7225c8 104 memmove(dest, source, prev - source);
5c19dc3a
A
105 dest += prev - source;
106 // 2) Skip remaining dupes
107 if (cur < end) {
108 while (cur += SOSDigestSize, cur < end) {
109 int delta = SOSDigestCompare(prev, cur);
110 assert(delta <= 0);
111 if (delta != 0)
112 break;
113 }
114 }
115 // cur now points to the first new element that hasn't yet been copied
116 // 3) Set start of next region
117 prev = cur;
118 source = cur;
119 }
120 }
121
122 // Copy remainder of final region
123 if (source < end) {
124 prev += SOSDigestSize;
125 if (dest != source)
fa7225c8 126 memmove(dest, source, prev - source);
5c19dc3a
A
127 dest += prev - source;
128 }
129 dv->count = (dest - dv->digest[0]) / SOSDigestSize;
130}
131
132
d8f41ccd
A
133void SOSDigestVectorSort(struct SOSDigestVector *dv)
134{
fa7225c8 135 if (dv->unsorted && dv->digest) {
d8f41ccd
A
136 qsort(dv->digest, dv->count, sizeof(*dv->digest), SOSDigestCompare);
137 dv->unsorted = false;
5c19dc3a 138 SOSDigestVectorUnique(dv);
d8f41ccd
A
139 }
140}
141
142void SOSDigestVectorUniqueSorted(struct SOSDigestVector *dv)
143{
5c19dc3a 144 // Uniqify in place (sort does this now for safety)
d8f41ccd
A
145 if (dv->unsorted)
146 SOSDigestVectorSort(dv);
d8f41ccd
A
147}
148
149void SOSDigestVectorSwap(struct SOSDigestVector *dva, struct SOSDigestVector *dvb)
150{
151 struct SOSDigestVector dv;
152 dv = *dva;
153 *dva = *dvb;
154 *dvb = dv;
155}
156
157bool SOSDigestVectorContainsSorted(const struct SOSDigestVector *dv, const uint8_t *digest)
158{
159 return SOSDigestVectorIndexOfSorted(dv, digest) != (size_t)-1;
160}
161
162bool SOSDigestVectorContains(struct SOSDigestVector *dv, const uint8_t *digest)
163{
164 if (dv->unsorted)
165 SOSDigestVectorSort(dv);
166 return SOSDigestVectorContainsSorted(dv, digest);
167}
168
169size_t SOSDigestVectorIndexOfSorted(const struct SOSDigestVector *dv, const uint8_t *digest)
170{
fa7225c8
A
171 if (dv->digest) {
172 const void *pos = bsearch(digest, dv->digest, dv->count, sizeof(*dv->digest), SOSDigestCompare);
173 return pos ? ((size_t)(pos - (void *)dv->digest)) / SOSDigestSize : ((size_t)-1);
174 } else {
175 return -1;
176 }
d8f41ccd
A
177}
178
179size_t SOSDigestVectorIndexOf(struct SOSDigestVector *dv, const uint8_t *digest)
180{
181 if (dv->unsorted)
182 SOSDigestVectorSort(dv);
183 return SOSDigestVectorIndexOfSorted(dv, digest);
184}
185
186void SOSDigestVectorFree(struct SOSDigestVector *dv)
187{
188 free(dv->digest);
189 dv->digest = NULL;
190 dv->count = 0;
191 dv->capacity = 0;
192 dv->unsorted = false;
193}
194
195void SOSDigestVectorApplySorted(const struct SOSDigestVector *dv, SOSDigestVectorApplyBlock with)
196{
197 bool stop = false;
fa7225c8 198 for (size_t ix = 0; !stop && ix < dv->count && dv->digest; ++ix) {
d8f41ccd
A
199 with(dv->digest[ix], &stop);
200 }
201}
202
203void SOSDigestVectorApply(struct SOSDigestVector *dv, SOSDigestVectorApplyBlock with)
204{
205 if (dv->unsorted)
206 SOSDigestVectorSort(dv);
207 SOSDigestVectorApplySorted(dv, with);
208}
209
210// TODO: Check for NDEBUG to disable skip dupes are release time.
211//#define SOSDVSKIPDUPES 0
212#define SOSDVSKIPDUPES 1
213
214#if SOSDVSKIPDUPES
215#define SOSDVINCRIX(dv,ix) (SOSDigestVectorIncrementAndSkipDupes(dv,ix))
d8f41ccd 216
5c19dc3a 217static size_t SOSIncrementAndSkipDupes(const uint8_t *digests, size_t count, const size_t ix) {
d8f41ccd 218 size_t new_ix = ix;
fa7225c8 219 if (digests && new_ix < count) {
5c19dc3a
A
220 while (++new_ix < count) {
221 int delta = SOSDigestCompare(digests + ix * SOSDigestSize, digests + new_ix * SOSDigestSize);
d8f41ccd
A
222 assert(delta <= 0);
223 if (delta != 0)
224 break;
225 }
226 }
227 return new_ix;
228}
229
5c19dc3a
A
230static size_t SOSDigestVectorIncrementAndSkipDupes(const struct SOSDigestVector *dv, const size_t ix) {
231 return SOSIncrementAndSkipDupes((const uint8_t *)dv->digest, dv->count, ix);
232}
233
d8f41ccd 234void SOSDigestVectorAppendMultipleOrdered(struct SOSDigestVector *dv,
5c19dc3a 235 size_t count, const uint8_t *digests) {
d8f41ccd
A
236 size_t ix = 0;
237 while (ix < count) {
238 SOSDigestVectorAppendOrdered(dv, digests + (ix * SOSDigestSize));
5c19dc3a 239 ix = SOSIncrementAndSkipDupes(digests, count, ix);
d8f41ccd 240 }
5c19dc3a
A
241}
242
243#else /* !SOSDVSKIPDUPES */
244
245#define SOSDVINCRIX(dv,ix) (ix + 1)
246
247void SOSDigestVectorAppendMultipleOrdered(struct SOSDigestVector *dv,
248 size_t count, const uint8_t *digests) {
d8f41ccd 249 if (count) {
fa7225c8
A
250 if (SOSDigestVectorEnsureCapacity(dv, dv->count + count))
251 memcpy(dv->digest[dv->count], digests, count * SOSDigestSize);
d8f41ccd
A
252 dv->count += count;
253 }
d8f41ccd
A
254}
255
5c19dc3a
A
256#endif /* !SOSDVSKIPDUPES */
257
d8f41ccd
A
258void SOSDigestVectorIntersectSorted(const struct SOSDigestVector *dv1, const struct SOSDigestVector *dv2,
259 struct SOSDigestVector *dvintersect)
260{
261 /* dvintersect should be empty to start. */
262 assert(dvintersect->count == 0);
263 size_t i1 = 0, i2 = 0;
264 while (i1 < dv1->count && i2 < dv2->count) {
265 int delta = SOSDigestCompare(dv1->digest[i1], dv2->digest[i2]);
266 if (delta == 0) {
267 SOSDigestVectorAppendOrdered(dvintersect, dv1->digest[i1]);
268 i1 = SOSDVINCRIX(dv1, i1);
269 i2 = SOSDVINCRIX(dv2, i2);
270 } else if (delta < 0) {
271 i1 = SOSDVINCRIX(dv1, i1);
272 } else {
273 i2 = SOSDVINCRIX(dv2, i2);
274 }
275 }
276}
277
278void SOSDigestVectorUnionSorted(const struct SOSDigestVector *dv1, const struct SOSDigestVector *dv2,
279 struct SOSDigestVector *dvunion)
280{
281 /* dvunion should be empty to start. */
282 assert(dvunion->count == 0);
283 size_t i1 = 0, i2 = 0;
284 while (i1 < dv1->count && i2 < dv2->count) {
285 int delta = SOSDigestCompare(dv1->digest[i1], dv2->digest[i2]);
286 if (delta == 0) {
287 SOSDigestVectorAppendOrdered(dvunion, dv1->digest[i1]);
288 i1 = SOSDVINCRIX(dv1, i1);
289 i2 = SOSDVINCRIX(dv2, i2);
290 } else if (delta < 0) {
291 SOSDigestVectorAppendOrdered(dvunion, dv1->digest[i1]);
292 i1 = SOSDVINCRIX(dv1, i1);
293 } else {
294 SOSDigestVectorAppendOrdered(dvunion, dv2->digest[i2]);
295 i2 = SOSDVINCRIX(dv2, i2);
296 }
297 }
298 SOSDigestVectorAppendMultipleOrdered(dvunion, dv1->count - i1, dv1->digest[i1]);
299 SOSDigestVectorAppendMultipleOrdered(dvunion, dv2->count - i2, dv2->digest[i2]);
300}
301
302void SOSDigestVectorDiffSorted(const struct SOSDigestVector *dv1, const struct SOSDigestVector *dv2,
303 struct SOSDigestVector *dv1_2, struct SOSDigestVector *dv2_1)
304{
305 /* dv1_2 and dv2_1 should be empty to start. */
306 assert(dv1_2->count == 0);
307 assert(dv2_1->count == 0);
308
309 size_t i1 = 0, i2 = 0;
310 while (i1 < dv1->count && i2 < dv2->count) {
311 int delta = SOSDigestCompare(dv1->digest[i1], dv2->digest[i2]);
312 if (delta == 0) {
313 i1 = SOSDVINCRIX(dv1, i1);
314 i2 = SOSDVINCRIX(dv2, i2);
315 } else if (delta < 0) {
316 SOSDigestVectorAppendOrdered(dv1_2, dv1->digest[i1]);
317 i1 = SOSDVINCRIX(dv1, i1);
318 } else {
319 SOSDigestVectorAppendOrdered(dv2_1, dv2->digest[i2]);
320 i2 = SOSDVINCRIX(dv2, i2);
321 }
322 }
323 SOSDigestVectorAppendMultipleOrdered(dv1_2, dv1->count - i1, dv1->digest[i1]);
324 SOSDigestVectorAppendMultipleOrdered(dv2_1, dv2->count - i2, dv2->digest[i2]);
325}
326
327void SOSDigestVectorDiff(struct SOSDigestVector *dv1, struct SOSDigestVector *dv2,
328 struct SOSDigestVector *dv1_2, struct SOSDigestVector *dv2_1)
329{
330 if (dv1->unsorted) SOSDigestVectorSort(dv1);
331 if (dv2->unsorted) SOSDigestVectorSort(dv2);
332 SOSDigestVectorDiffSorted(dv1, dv2, dv1_2, dv2_1);
333}
334
335/*
336 If A and B are sets, then the relative complement of A in B, also termed the set-theoretic difference of B and A,
337 is the set of elements in B, but not in A. The relative complement of A in B is denoted B ∖ A according to the ISO 31-11 standard
338 sometimes written B − A
339
340 The common case for us will be Removals\Additions
341 */
342
343static void SOSDigestVectorAppendComplementAtIndex(size_t a_ix, const struct SOSDigestVector *dvA, size_t b_ix, const struct SOSDigestVector *dvB,
344 struct SOSDigestVector *dvcomplement)
345{
346 assert(a_ix <= dvA->count && b_ix <= dvB->count);
fa7225c8 347 while (a_ix < dvA->count && b_ix < dvB->count && dvA->digest && dvB->digest) {
d8f41ccd
A
348 int delta = SOSDigestCompare(dvA->digest[a_ix], dvB->digest[b_ix]);
349 if (delta == 0) {
350 a_ix = SOSDVINCRIX(dvA, a_ix);
351 b_ix = SOSDVINCRIX(dvB, b_ix);
352 } else if (delta < 0) {
353 a_ix = SOSDVINCRIX(dvA, a_ix);
354 } else {
355 SOSDigestVectorAppendOrdered(dvcomplement, dvB->digest[b_ix]);
356 b_ix = SOSDVINCRIX(dvB, b_ix);
357 }
358 }
fa7225c8
A
359 if (dvB->digest)
360 SOSDigestVectorAppendMultipleOrdered(dvcomplement, dvB->count - b_ix, dvB->digest[b_ix]);
d8f41ccd
A
361}
362
363
364void SOSDigestVectorComplementSorted(const struct SOSDigestVector *dvA, const struct SOSDigestVector *dvB,
365 struct SOSDigestVector *dvcomplement)
366{
367 /* dvcomplement should be empty to start. */
368 assert(dvcomplement->count == 0);
369 assert(!dvA->unsorted);
370 assert(!dvB->unsorted);
371
372 SOSDigestVectorAppendComplementAtIndex(0, dvA, 0, dvB, dvcomplement);
373}
374
375
376/*
377 For each item in base
378
379 one way to do would be to define SOSDigestVectorComplementSorted
380
381 For removals, if removal value is less than base, increment until GEQ
382 */
383bool SOSDigestVectorPatchSorted(const struct SOSDigestVector *base, const struct SOSDigestVector *removals,
384 const struct SOSDigestVector *additions, struct SOSDigestVector *dv,
385 CFErrorRef *error)
386{
387 /* dv should be empty to start. */
388 assert(dv->count == 0);
389 assert(!base->unsorted);
390 assert(!removals->unsorted);
391 assert(!additions->unsorted);
392
393 size_t i1 = 0, i2 = 0, i3 = 0;
394 while (i1 < base->count && i2 < additions->count) {
395 // Pick the smaller of base->digest[i1] and additions->digest[i2] as a
396 // candidate to be put into the output vector. If udelta positive, addition is smaller
397 int udelta = SOSDigestCompare(base->digest[i1], additions->digest[i2]);
398 const uint8_t *candidate = udelta < 0 ? base->digest[i1] : additions->digest[i2];
399
400 // ddelta > 0 means rem > candidate
401 int ddelta = 1;
402 while (i3 < removals->count) {
403 ddelta = SOSDigestCompare(removals->digest[i3], candidate);
404 if (ddelta < 0) {
405 i3 = SOSDVINCRIX(removals, i3);
406 } else {
407 if (ddelta == 0)
408 i3 = SOSDVINCRIX(removals, i3);
409 break;
410 }
411 }
412 if (ddelta > 0)
413 SOSDigestVectorAppendOrdered(dv, candidate);
414
415 // Point to next (different) candidate
416 if (udelta == 0) {
417 i1 = SOSDVINCRIX(base, i1);
418 i2 = SOSDVINCRIX(additions, i2);
419 } else if (udelta < 0) {
420 i1 = SOSDVINCRIX(base, i1);
421 } else {
422 i2 = SOSDVINCRIX(additions, i2);
423 }
424 }
425 SOSDigestVectorAppendComplementAtIndex(i3, removals, i1, base, dv);
426 SOSDigestVectorAppendComplementAtIndex(i3, removals, i2, additions, dv);
427
428 return true;
429}
430
431bool SOSDigestVectorPatch(struct SOSDigestVector *base, struct SOSDigestVector *removals,
432 struct SOSDigestVector *additions, struct SOSDigestVector *dv,
433 CFErrorRef *error)
434{
435 if (base->unsorted) SOSDigestVectorSort(base);
436 if (removals->unsorted) SOSDigestVectorSort(removals);
437 if (additions->unsorted) SOSDigestVectorSort(additions);
438 return SOSDigestVectorPatchSorted(base, removals, additions, dv, error);
439}