2 * Copyright (c) 2013-2014 Apple Inc. All Rights Reserved.
4 * @APPLE_LICENSE_HEADER_START@
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
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.
21 * @APPLE_LICENSE_HEADER_END@
25 #include <SecureObjectSync/SOSDigestVector.h>
26 #include <utilities/SecCFError.h>
27 #include <utilities/SecCFWrappers.h>
28 #include <utilities/comparison.h>
29 #include <dispatch/dispatch.h>
32 CFStringRef kSOSDigestVectorErrorDomain
= CFSTR("com.apple.security.sos.digestvector.error");
34 /* SOSDigestVector code. */
36 #define VECTOR_GROW(vector, count, capacity) \
38 if ((count) > capacity) { \
39 capacity = ((capacity) + 16) * 3 / 2; \
40 if (capacity < (count)) \
42 vector = reallocf((vector), sizeof(*(vector)) * capacity); \
46 static void SOSDigestVectorEnsureCapacity(struct SOSDigestVector
*dv
, size_t count
) {
47 VECTOR_GROW(dv
->digest
, count
, dv
->capacity
);
50 void SOSDigestVectorReplaceAtIndex(struct SOSDigestVector
*dv
, size_t ix
, const uint8_t *digest
)
52 SOSDigestVectorEnsureCapacity(dv
, ix
+ 1);
53 memcpy(dv
->digest
[ix
], digest
, SOSDigestSize
);
57 static void SOSDigestVectorAppendOrdered(struct SOSDigestVector
*dv
, const uint8_t *digest
)
59 SOSDigestVectorEnsureCapacity(dv
, dv
->count
+ 1);
60 memcpy(dv
->digest
[dv
->count
++], digest
, SOSDigestSize
);
63 void SOSDigestVectorAppend(struct SOSDigestVector
*dv
, const uint8_t *digest
)
65 SOSDigestVectorAppendOrdered(dv
, digest
);
69 static int SOSDigestCompare(const void *a
, const void *b
)
71 return memcmp(a
, b
, SOSDigestSize
);
74 void SOSDigestVectorSort(struct SOSDigestVector
*dv
)
77 qsort(dv
->digest
, dv
->count
, sizeof(*dv
->digest
), SOSDigestCompare
);
82 void SOSDigestVectorUniqueSorted(struct SOSDigestVector
*dv
)
85 // TODO: This is really inefficient because of all the memcpys
87 SOSDigestVectorSort(dv
);
90 uint8_t *prevDigest
= dv
->digest
[0];
91 for (idx
= 1; idx
< (int)dv
->count
; idx
++)
93 if (SOSDigestCompare(prevDigest
, dv
->digest
[idx
])) { // this element is not the same as previous one
94 SOSDigestVectorReplaceAtIndex(dv
, odx
, dv
->digest
[idx
]);
95 prevDigest
= dv
->digest
[odx
];
102 void SOSDigestVectorSwap(struct SOSDigestVector
*dva
, struct SOSDigestVector
*dvb
)
104 struct SOSDigestVector dv
;
110 bool SOSDigestVectorContainsSorted(const struct SOSDigestVector
*dv
, const uint8_t *digest
)
112 return SOSDigestVectorIndexOfSorted(dv
, digest
) != (size_t)-1;
115 bool SOSDigestVectorContains(struct SOSDigestVector
*dv
, const uint8_t *digest
)
118 SOSDigestVectorSort(dv
);
119 return SOSDigestVectorContainsSorted(dv
, digest
);
122 size_t SOSDigestVectorIndexOfSorted(const struct SOSDigestVector
*dv
, const uint8_t *digest
)
124 const void *pos
= bsearch(digest
, dv
->digest
, dv
->count
, sizeof(*dv
->digest
), SOSDigestCompare
);
125 return pos
? ((size_t)(pos
- (void *)dv
->digest
)) / SOSDigestSize
: ((size_t)-1);
128 size_t SOSDigestVectorIndexOf(struct SOSDigestVector
*dv
, const uint8_t *digest
)
131 SOSDigestVectorSort(dv
);
132 return SOSDigestVectorIndexOfSorted(dv
, digest
);
135 void SOSDigestVectorFree(struct SOSDigestVector
*dv
)
141 dv
->unsorted
= false;
144 void SOSDigestVectorApplySorted(const struct SOSDigestVector
*dv
, SOSDigestVectorApplyBlock with
)
147 for (size_t ix
= 0; !stop
&& ix
< dv
->count
; ++ix
) {
148 with(dv
->digest
[ix
], &stop
);
152 void SOSDigestVectorApply(struct SOSDigestVector
*dv
, SOSDigestVectorApplyBlock with
)
155 SOSDigestVectorSort(dv
);
156 SOSDigestVectorApplySorted(dv
, with
);
159 // TODO: Check for NDEBUG to disable skip dupes are release time.
160 //#define SOSDVSKIPDUPES 0
161 #define SOSDVSKIPDUPES 1
164 #define SOSDVINCRIX(dv,ix) (SOSDigestVectorIncrementAndSkipDupes(dv,ix))
166 #define SOSDVINCRIX(dv,ix) (ix + 1)
169 static size_t SOSDigestVectorIncrementAndSkipDupes(const struct SOSDigestVector
*dv
, const size_t ix
) {
171 if (new_ix
< dv
->count
) {
172 while (++new_ix
< dv
->count
) {
173 int delta
= SOSDigestCompare(dv
->digest
[ix
], dv
->digest
[new_ix
]);
182 void SOSDigestVectorAppendMultipleOrdered(struct SOSDigestVector
*dv
,
183 size_t count
, const uint8_t *digests
) {
187 SOSDigestVectorAppendOrdered(dv
, digests
+ (ix
* SOSDigestSize
));
188 ix
= SOSDVINCRIX(dv
, ix
);
192 SOSDigestVectorEnsureCapacity(dv
, dv
->count
+ count
);
193 memcpy(dv
->digest
[dv
->count
], digests
, count
* SOSDigestSize
);
199 void SOSDigestVectorIntersectSorted(const struct SOSDigestVector
*dv1
, const struct SOSDigestVector
*dv2
,
200 struct SOSDigestVector
*dvintersect
)
202 /* dvintersect should be empty to start. */
203 assert(dvintersect
->count
== 0);
204 size_t i1
= 0, i2
= 0;
205 while (i1
< dv1
->count
&& i2
< dv2
->count
) {
206 int delta
= SOSDigestCompare(dv1
->digest
[i1
], dv2
->digest
[i2
]);
208 SOSDigestVectorAppendOrdered(dvintersect
, dv1
->digest
[i1
]);
209 i1
= SOSDVINCRIX(dv1
, i1
);
210 i2
= SOSDVINCRIX(dv2
, i2
);
211 } else if (delta
< 0) {
212 i1
= SOSDVINCRIX(dv1
, i1
);
214 i2
= SOSDVINCRIX(dv2
, i2
);
219 void SOSDigestVectorUnionSorted(const struct SOSDigestVector
*dv1
, const struct SOSDigestVector
*dv2
,
220 struct SOSDigestVector
*dvunion
)
222 /* dvunion should be empty to start. */
223 assert(dvunion
->count
== 0);
224 size_t i1
= 0, i2
= 0;
225 while (i1
< dv1
->count
&& i2
< dv2
->count
) {
226 int delta
= SOSDigestCompare(dv1
->digest
[i1
], dv2
->digest
[i2
]);
228 SOSDigestVectorAppendOrdered(dvunion
, dv1
->digest
[i1
]);
229 i1
= SOSDVINCRIX(dv1
, i1
);
230 i2
= SOSDVINCRIX(dv2
, i2
);
231 } else if (delta
< 0) {
232 SOSDigestVectorAppendOrdered(dvunion
, dv1
->digest
[i1
]);
233 i1
= SOSDVINCRIX(dv1
, i1
);
235 SOSDigestVectorAppendOrdered(dvunion
, dv2
->digest
[i2
]);
236 i2
= SOSDVINCRIX(dv2
, i2
);
239 SOSDigestVectorAppendMultipleOrdered(dvunion
, dv1
->count
- i1
, dv1
->digest
[i1
]);
240 SOSDigestVectorAppendMultipleOrdered(dvunion
, dv2
->count
- i2
, dv2
->digest
[i2
]);
243 void SOSDigestVectorDiffSorted(const struct SOSDigestVector
*dv1
, const struct SOSDigestVector
*dv2
,
244 struct SOSDigestVector
*dv1_2
, struct SOSDigestVector
*dv2_1
)
246 /* dv1_2 and dv2_1 should be empty to start. */
247 assert(dv1_2
->count
== 0);
248 assert(dv2_1
->count
== 0);
250 size_t i1
= 0, i2
= 0;
251 while (i1
< dv1
->count
&& i2
< dv2
->count
) {
252 int delta
= SOSDigestCompare(dv1
->digest
[i1
], dv2
->digest
[i2
]);
254 i1
= SOSDVINCRIX(dv1
, i1
);
255 i2
= SOSDVINCRIX(dv2
, i2
);
256 } else if (delta
< 0) {
257 SOSDigestVectorAppendOrdered(dv1_2
, dv1
->digest
[i1
]);
258 i1
= SOSDVINCRIX(dv1
, i1
);
260 SOSDigestVectorAppendOrdered(dv2_1
, dv2
->digest
[i2
]);
261 i2
= SOSDVINCRIX(dv2
, i2
);
264 SOSDigestVectorAppendMultipleOrdered(dv1_2
, dv1
->count
- i1
, dv1
->digest
[i1
]);
265 SOSDigestVectorAppendMultipleOrdered(dv2_1
, dv2
->count
- i2
, dv2
->digest
[i2
]);
268 void SOSDigestVectorDiff(struct SOSDigestVector
*dv1
, struct SOSDigestVector
*dv2
,
269 struct SOSDigestVector
*dv1_2
, struct SOSDigestVector
*dv2_1
)
271 if (dv1
->unsorted
) SOSDigestVectorSort(dv1
);
272 if (dv2
->unsorted
) SOSDigestVectorSort(dv2
);
273 SOSDigestVectorDiffSorted(dv1
, dv2
, dv1_2
, dv2_1
);
277 If A and B are sets, then the relative complement of A in B, also termed the set-theoretic difference of B and A,
278 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
279 sometimes written B − A
281 The common case for us will be Removals\Additions
284 static void SOSDigestVectorAppendComplementAtIndex(size_t a_ix
, const struct SOSDigestVector
*dvA
, size_t b_ix
, const struct SOSDigestVector
*dvB
,
285 struct SOSDigestVector
*dvcomplement
)
287 assert(a_ix
<= dvA
->count
&& b_ix
<= dvB
->count
);
288 while (a_ix
< dvA
->count
&& b_ix
< dvB
->count
) {
289 int delta
= SOSDigestCompare(dvA
->digest
[a_ix
], dvB
->digest
[b_ix
]);
291 a_ix
= SOSDVINCRIX(dvA
, a_ix
);
292 b_ix
= SOSDVINCRIX(dvB
, b_ix
);
293 } else if (delta
< 0) {
294 a_ix
= SOSDVINCRIX(dvA
, a_ix
);
296 SOSDigestVectorAppendOrdered(dvcomplement
, dvB
->digest
[b_ix
]);
297 b_ix
= SOSDVINCRIX(dvB
, b_ix
);
300 SOSDigestVectorAppendMultipleOrdered(dvcomplement
, dvB
->count
- b_ix
, dvB
->digest
[b_ix
]);
304 void SOSDigestVectorComplementSorted(const struct SOSDigestVector
*dvA
, const struct SOSDigestVector
*dvB
,
305 struct SOSDigestVector
*dvcomplement
)
307 /* dvcomplement should be empty to start. */
308 assert(dvcomplement
->count
== 0);
309 assert(!dvA
->unsorted
);
310 assert(!dvB
->unsorted
);
312 SOSDigestVectorAppendComplementAtIndex(0, dvA
, 0, dvB
, dvcomplement
);
317 For each item in base
319 one way to do would be to define SOSDigestVectorComplementSorted
321 For removals, if removal value is less than base, increment until GEQ
323 bool SOSDigestVectorPatchSorted(const struct SOSDigestVector
*base
, const struct SOSDigestVector
*removals
,
324 const struct SOSDigestVector
*additions
, struct SOSDigestVector
*dv
,
327 /* dv should be empty to start. */
328 assert(dv
->count
== 0);
329 assert(!base
->unsorted
);
330 assert(!removals
->unsorted
);
331 assert(!additions
->unsorted
);
333 size_t i1
= 0, i2
= 0, i3
= 0;
334 while (i1
< base
->count
&& i2
< additions
->count
) {
335 // Pick the smaller of base->digest[i1] and additions->digest[i2] as a
336 // candidate to be put into the output vector. If udelta positive, addition is smaller
337 int udelta
= SOSDigestCompare(base
->digest
[i1
], additions
->digest
[i2
]);
338 const uint8_t *candidate
= udelta
< 0 ? base
->digest
[i1
] : additions
->digest
[i2
];
340 // ddelta > 0 means rem > candidate
342 while (i3
< removals
->count
) {
343 ddelta
= SOSDigestCompare(removals
->digest
[i3
], candidate
);
345 i3
= SOSDVINCRIX(removals
, i3
);
348 i3
= SOSDVINCRIX(removals
, i3
);
353 SOSDigestVectorAppendOrdered(dv
, candidate
);
355 // Point to next (different) candidate
357 i1
= SOSDVINCRIX(base
, i1
);
358 i2
= SOSDVINCRIX(additions
, i2
);
359 } else if (udelta
< 0) {
360 i1
= SOSDVINCRIX(base
, i1
);
362 i2
= SOSDVINCRIX(additions
, i2
);
365 SOSDigestVectorAppendComplementAtIndex(i3
, removals
, i1
, base
, dv
);
366 SOSDigestVectorAppendComplementAtIndex(i3
, removals
, i2
, additions
, dv
);
371 bool SOSDigestVectorPatch(struct SOSDigestVector
*base
, struct SOSDigestVector
*removals
,
372 struct SOSDigestVector
*additions
, struct SOSDigestVector
*dv
,
375 if (base
->unsorted
) SOSDigestVectorSort(base
);
376 if (removals
->unsorted
) SOSDigestVectorSort(removals
);
377 if (additions
->unsorted
) SOSDigestVectorSort(additions
);
378 return SOSDigestVectorPatchSorted(base
, removals
, additions
, dv
, error
);