]>
Commit | Line | Data |
---|---|---|
d8f41ccd A |
1 | /* |
2 | * Copyright (c) 2013-2014 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 | ||
25 | /* | |
26 | * SOSMessage.c - Creation and decoding of SOSMessage objects. | |
27 | */ | |
28 | ||
5c19dc3a | 29 | #include <Security/SecureObjectSync/SOSMessage.h> |
d8f41ccd A |
30 | |
31 | #include <AssertMacros.h> | |
32 | #include <CoreFoundation/CoreFoundation.h> | |
5c19dc3a A |
33 | #include <Security/SecureObjectSync/SOSDigestVector.h> |
34 | #include <Security/SecureObjectSync/SOSManifest.h> | |
35 | #include <Security/SecureObjectSync/SOSInternal.h> | |
d8f41ccd A |
36 | #include <corecrypto/ccder.h> |
37 | #include <stdlib.h> | |
38 | #include <stdbool.h> | |
39 | #include <utilities/SecCFError.h> | |
40 | #include <utilities/SecCFRelease.h> | |
41 | #include <utilities/SecCFWrappers.h> | |
42 | #include <utilities/array_size.h> | |
43 | #include <utilities/der_date.h> | |
44 | #include <utilities/der_plist.h> | |
45 | #include <utilities/der_plist_internal.h> | |
46 | #include <utilities/debugging.h> | |
47 | #include <utilities/iCloudKeychainTrace.h> | |
48 | ||
49 | // TODO: This is a layer violation, we need a better way to do this | |
50 | // Currently it's only used for logging. | |
51 | #include <securityd/SecItemDataSource.h> | |
52 | ||
53 | #if defined(SOSMessageFormatSpecification) && 0 | |
54 | ||
55 | -- Secure Object Syncing Peer to Peer Message format ASN.1 definition | |
56 | -- Everything MUST be DER encoded unless otherwise noted. These exceptions | |
57 | -- Allow us to stream messages on a streamy network, without loading more | |
58 | -- than one object into memory at once. | |
59 | ||
60 | SOSMessage := SEQUENCE { | |
61 | CHOICE { | |
62 | v0 V0-MESSAGE-BODY-CLASS | |
63 | v2 SOSV2MessageBody | |
64 | } | |
65 | } | |
66 | ||
67 | -- v0 Message | |
68 | ||
69 | V0-MESSAGE-BODY-CLASS ::= CLASS | |
70 | { | |
71 | &messageType INTEGER (manifestDigest, manifest, manifestDeltaAndObjects) | |
72 | &version INTEGER OPTIONAL default v0 | |
73 | &Type | |
74 | } | |
75 | WITH SYNTAX {&Type IDENTIFIED BY &messageType} | |
76 | ||
77 | ManifestDigest ::= OCTECT STRING (length 20) | |
78 | ||
79 | Manifest ::= OCTECT STRING -- (length 20 * number of entries) | |
80 | ||
81 | manifestDigestBody ::= | |
82 | { ManifestDigest IDENTIFIED BY {manifestDigest}} | |
83 | ||
84 | manifestBody ::= | |
85 | { Manifest IDENTIFIED BY {manifest}} | |
86 | ||
87 | manifestDeltaAndObjectsBody ::= | |
88 | { ManifestDeltaAndObjects IDENTIFIED BY {manifestDeltaAndObjects}} | |
89 | ||
90 | SOSV1MessageBody ::= MESSAGE-BODY-CLASS | |
91 | ||
92 | ManifestDeltaAndObjects ::= SEQUENCE { | |
93 | manfestDigest ManifestDigest | |
94 | removals Manifest | |
95 | additions Manifest | |
96 | addedObjects SEQUENCE OF SOSObject | |
97 | } | |
98 | ||
99 | -- v2 Message | |
100 | ||
101 | SOSMessageBody := { | |
102 | -- top level SEQUENCE may be Constructed, indefinite-length BER encoded | |
103 | header SOSMessageHeader, | |
104 | deltas [0] IMPLICIT SOSManifestDeltas OPTIONAL, | |
105 | extensions [1] IMPLICIT SOSExtensions OPTIONAL, | |
106 | objects [2] IMPLICIT SEQUENCE OF SOSObject OPTIONAL | |
107 | -- [2] IMPLICIT SEQUENCE OF SOSObject may be Constructed, | |
108 | -- indefinite-length BER encoded -- } | |
109 | ||
110 | SOSMessageHeader ::= SEQUENCE { | |
111 | version [0] IMPLICIT SOSMessageVersion DEFAULT v2, | |
112 | creationTime GeneralizedTime OPTIONAL, | |
113 | -- When this message was created by the sender for tracking latency | |
114 | sequenceNumber SOSSequenceNumber OPTIONAL, | |
115 | -- Message Sequence Number for tracking packet loss in transport | |
116 | digestTypes SOSDigestTypes OPTIONAL, | |
117 | -- Determines the size and format of each SOSManifestDigest and the | |
118 | -- elements of each SOSManifest. | |
119 | -- We send the intersection our desired SOSDigestTypes and our peers | |
120 | -- last received SOSDigestType. If we never received a message from our | |
121 | -- peer we send our entire desired set and set the digestTypesProposed | |
122 | -- messageFlag. | |
123 | -- If the intersection is the empty set we fallback to sha1 | |
124 | -- Each digest and manifest entry is constructed by appending the | |
125 | -- agreed upon digests in the order they are listed in the DER encoded | |
126 | -- digestTypes. | |
127 | messageFlags BIT STRING { | |
128 | getObjects (0), | |
129 | joinRequest (1), | |
130 | partial (2), | |
131 | digestTypesProposed (3), | |
132 | -- This is a partial update and might not contain accurate manifest deltas (check this against spec --mb), only objects | |
133 | clearGetObjects (4), -- WIP mb ignore | |
134 | -- Stop sending me objects for this delta update, I will send you mine instead if you give me a full manifest delta | |
135 | didClearGetObjectsSinceLastDelta (5) -- WIP mb ignore | |
136 | -- clearGetObjects was set during this delta update, do not | |
137 | -- set it again (STICKY until either peer clears delta) -- } | |
138 | skipHello (6) -- Respond with at least a manifest | |
139 | senderDigest SOSManifestDigest, | |
140 | -- The senders manifest digest at the time of sending this message. | |
141 | baseDigest [0] IMPLICIT SOSManifestDigest, | |
142 | -- What this message is based on, if it contains deltas. If missing we assume the empty set | |
143 | proposedDigest [1] IMPLICIT SOSManifestDigest, | |
144 | -- What the receiver should have after patching baseDigest with | |
145 | -- additions and removals -- } | |
146 | ||
147 | SOSMessageVersion ::= INTEGER { v0(0), v2(2), v3(3) } | |
148 | ||
149 | SOSSequenceNumber ::= INTEGER | |
150 | ||
151 | -- Note this is not implemented in v2 it only supports sha1 | |
152 | SOSDigestTypes ::= SEQUENCE { | |
153 | messageFlags BIT STRING { | |
154 | sha1(0) -- implied if SOSDigestTypes is not present | |
155 | sha224(1) | |
156 | sha256(2) | |
157 | sha384(3) | |
158 | sha512(4) | |
159 | digestAlgorithms SET OF AlgorithmIdentifier | |
160 | -- Same as AlgorithmIdentifier from X.509 -- } } | |
161 | ||
162 | SOSManifestDeltas ::= SEQUENCE { | |
163 | removals SOSManifest | |
164 | additions SOSManifest } | |
165 | ||
166 | SOSExtensions ::= SEQUENCE SIZE (1..MAX) OF SOSExtension | |
167 | ||
168 | SOSExtension ::= SEQUENCE { | |
169 | extnID OBJECT IDENTIFIER, | |
170 | critical BOOLEAN DEFAULT FALSE, | |
171 | extnValue OCTET STRING } | |
172 | ||
173 | SOSManifest ::= OCTET STRING | |
174 | -- DER encoding is sorted and ready to merge. | |
175 | -- All SOSDigest entries in a SOSManifest /must/ be the same size | |
176 | -- As the negotiated SOSManifestEntry. Se comment in SOSMessageBody | |
177 | -- on digestTypes | |
178 | ||
179 | SOSManifestDigest ::= OCTET STRING | |
180 | ||
181 | SOSObject ::= SEQUENCE { | |
182 | [0] conflict OCTECT STRING OPTIONAL | |
183 | [1] change OCTECT STRING OPTIONAL | |
184 | object SecDictionary } | |
185 | ||
186 | SecDictionary ::= SET of SecKVPair | |
187 | ||
188 | SecKVPair ::= SEQUENCE { | |
189 | key UTF8String | |
190 | value Value } | |
191 | ||
192 | SecValue ::= CHOICE { | |
193 | bool Boolean | |
194 | number INTEGER | |
195 | string UTF8String | |
196 | data OCTECT STRING | |
197 | date GENERAL TIME | |
198 | dictionary Object | |
199 | array Array } | |
200 | ||
201 | SecArray ::= SEQUENCE of SecValue | |
202 | ||
203 | -- For reference: | |
204 | AlgorithmIdentifier ::= SEQUENCE { | |
205 | algorithm OBJECT IDENTIFIER, | |
206 | parameters ANY DEFINED BY algorithm OPTIONAL } | |
207 | -- contains a value of the type | |
208 | -- registered for use with the | |
209 | -- algorithm object identifier value | |
210 | ||
211 | #endif // defined(SOSMessageFormatSpecification) && 0 | |
212 | ||
213 | ||
214 | #if 0 | |
215 | static inline bool SecMallocOk(const void *ptr) { | |
216 | if (ptr) return true; | |
217 | ||
218 | return false; | |
219 | } | |
220 | #endif | |
221 | #if 0 | |
222 | static void appendObjects(CFMutableStringRef desc, CFArrayRef objects) { | |
223 | __block bool needComma = false; | |
224 | CFArrayForEach(objects, ^(const void *value) { | |
225 | if (needComma) | |
226 | CFStringAppend(desc, CFSTR(",")); | |
227 | else | |
228 | needComma = true; | |
229 | ||
230 | SecItemServerAppendItemDescription(desc, value); | |
231 | }); | |
232 | } | |
233 | #endif | |
234 | ||
235 | ||
236 | ||
237 | // | |
238 | // MARK: SOSMessage implementation. | |
239 | // | |
240 | ||
241 | // Legacy v1 message type numbers | |
242 | enum SOSMessageType { | |
243 | SOSManifestInvalidMessageType = 0, | |
244 | SOSManifestDigestMessageType = 1, | |
245 | SOSManifestMessageType = 2, | |
246 | SOSManifestDeltaAndObjectsMessageType = 3, | |
247 | }; | |
248 | ||
249 | struct __OpaqueSOSMessage { | |
250 | CFRuntimeBase _base; | |
251 | ||
252 | CFDataRef der; | |
253 | const uint8_t *objectsDer; | |
254 | size_t objectsLen; | |
255 | ||
256 | CFDataRef senderDigest; | |
257 | CFDataRef baseDigest; | |
258 | CFDataRef proposedDigest; | |
259 | SOSManifestRef removals; | |
260 | SOSManifestRef additions; | |
261 | ||
262 | CFMutableArrayRef objects; | |
263 | ||
264 | SOSMessageFlags flags; | |
265 | uint64_t sequenceNumber; | |
266 | CFAbsoluteTime creationTime; | |
267 | uint64_t version; // Message version (currently always 2) | |
268 | bool indefiniteLength; // If set to true the top SEQUENCE and the OBJECTS SEQUENCE are written indefinite length. | |
269 | }; | |
270 | ||
271 | CFGiblisWithCompareFor(SOSMessage) | |
272 | ||
273 | static Boolean SOSMessageCompare(CFTypeRef cf1, CFTypeRef cf2) { | |
274 | SOSMessageRef M = (SOSMessageRef)cf1; | |
275 | SOSMessageRef P = (SOSMessageRef)cf2; | |
276 | if (M->flags != P->flags) return false; | |
277 | if (M->sequenceNumber != P->sequenceNumber) return false; | |
278 | if (M->creationTime != P->creationTime) return false; | |
279 | //if (!CFEqualSafe(M->der, P->der)) return false; | |
280 | if (!CFEqualSafe(M->senderDigest, P->senderDigest)) return false; | |
281 | if (!CFEqualSafe(M->baseDigest, P->baseDigest)) return false; | |
282 | if (!CFEqualSafe(M->proposedDigest, P->proposedDigest)) return false; | |
283 | if (!CFEqualSafe(M->removals, P->removals)) return false; | |
284 | if (!CFEqualSafe(M->additions, P->additions)) return false; | |
285 | ||
286 | // TODO Compare Objects if present. | |
287 | ||
288 | return true; | |
289 | } | |
290 | ||
291 | static void SOSMessageDestroy(CFTypeRef cf) { | |
292 | SOSMessageRef message = (SOSMessageRef)cf; | |
293 | CFReleaseNull(message->der); | |
294 | CFReleaseNull(message->senderDigest); | |
295 | CFReleaseNull(message->baseDigest); | |
296 | CFReleaseNull(message->proposedDigest); | |
297 | CFReleaseNull(message->additions); | |
298 | CFReleaseNull(message->removals); | |
299 | CFReleaseNull(message->objects); | |
300 | } | |
301 | ||
302 | // TODO: Remove this layer violation! | |
303 | #include <securityd/SecItemServer.h> | |
304 | ||
305 | static uint64_t SOSMessageInferType(SOSMessageRef message, CFErrorRef *error); | |
306 | ||
d87e1158 | 307 | static CFStringRef SOSMessageCopyFormatDescription(CFTypeRef cf, CFDictionaryRef formatOptions) { |
d8f41ccd A |
308 | SOSMessageRef message = (SOSMessageRef)cf; |
309 | static const uint8_t zero[4] = {}; | |
310 | const uint8_t *S = message->senderDigest ? CFDataGetBytePtr(message->senderDigest) : zero; | |
311 | const uint8_t *B = message->baseDigest ? CFDataGetBytePtr(message->baseDigest) : zero; | |
312 | const uint8_t *P = message->proposedDigest ? CFDataGetBytePtr(message->proposedDigest) : zero; | |
313 | CFDateRef creationDate = CFDateCreate(CFGetAllocator(message), message->creationTime); | |
314 | ||
315 | CFMutableStringRef objects = CFStringCreateMutable(kCFAllocatorDefault, 0); | |
316 | ||
317 | // TODO: Remove this layer violation! | |
318 | SOSDataSourceFactoryRef dsf = SecItemDataSourceFactoryGetDefault(); | |
5c19dc3a | 319 | SOSDataSourceRef ds = SOSDataSourceFactoryCreateDataSource(dsf, kSecAttrAccessibleWhenUnlocked, NULL); |
d8f41ccd | 320 | |
6b200bc3 A |
321 | if (ds) { |
322 | __block size_t maxEntries = 16; | |
323 | CFStringAppendFormat(objects, NULL, CFSTR("{[%zu]"), SOSMessageCountObjects(message)); | |
324 | SOSMessageWithSOSObjects(message, ds, NULL, ^(SOSObjectRef object, bool *stop) { | |
325 | CFDataRef digest = SOSObjectCopyDigest(ds, object, NULL); | |
326 | const uint8_t *O = CFDataGetBytePtr(digest); | |
327 | CFStringAppendFormat(objects, NULL, CFSTR(" %02X%02X%02X%02X"), O[0],O[1],O[2],O[3]); | |
328 | CFReleaseSafe(digest); | |
329 | if (!--maxEntries) { | |
330 | CFStringAppend(objects, CFSTR("...")); | |
331 | *stop = true; | |
332 | } | |
333 | }); | |
334 | CFStringAppend(objects, CFSTR("}")); | |
335 | } else { | |
336 | CFStringAppend(objects, CFSTR("{NO DATASOURCE}")); | |
337 | } | |
d8f41ccd | 338 | |
6b200bc3 | 339 | CFStringRef desc = NULL; |
d8f41ccd A |
340 | if (message->version == 0) { |
341 | switch (SOSMessageInferType(message, NULL)) { | |
342 | case SOSManifestInvalidMessageType: | |
343 | desc = CFStringCreateWithFormat(CFGetAllocator(message), NULL, CFSTR("<MSGInvalid %"PRIu64" >"), message->sequenceNumber); | |
344 | break; | |
345 | case SOSManifestDigestMessageType: | |
346 | desc = CFStringCreateWithFormat(CFGetAllocator(message), NULL, CFSTR("<MSGDigest %"PRIu64" %02X%02X%02X%02X>"), message->sequenceNumber, S[0],S[1],S[2],S[3]); | |
347 | break; | |
348 | case SOSManifestMessageType: | |
349 | desc = CFStringCreateWithFormat(CFGetAllocator(message), NULL, CFSTR("<MSGManifest %"PRIu64" %@>"), message->sequenceNumber, message->additions); | |
350 | break; | |
351 | case SOSManifestDeltaAndObjectsMessageType: | |
352 | desc = CFStringCreateWithFormat(CFGetAllocator(message), NULL, CFSTR("<MSGObjects %"PRIu64" %02X%02X%02X%02X %@ %@ %@"), | |
353 | message->sequenceNumber, | |
354 | B[0],B[1],B[2],B[3], | |
355 | message->removals, message->additions, | |
356 | objects); | |
357 | break; | |
358 | } | |
359 | } else { | |
360 | desc = CFStringCreateWithFormat | |
361 | (CFGetAllocator(message), NULL, CFSTR("<MSG %"PRIu64" %@ %02X%02X%02X%02X %02X%02X%02X%02X %02X%02X%02X%02X %@ %@ %@ %s%s%s%s%s%s%s>"), | |
362 | message->sequenceNumber, | |
363 | creationDate, | |
364 | S[0],S[1],S[2],S[3], | |
365 | B[0],B[1],B[2],B[3], | |
366 | P[0],P[1],P[2],P[3], | |
367 | message->removals, message->additions, | |
368 | objects, | |
369 | (message->flags & kSOSMessageGetObjects) ? "G" : "g", | |
370 | (message->flags & kSOSMessageJoinRequest) ? "J" : "j", | |
371 | (message->flags & kSOSMessagePartial) ? "P" : "p", | |
372 | (message->flags & kSOSMessageDigestTypesProposed) ? "D" : "d", | |
373 | (message->flags & kSOSMessageClearGetObjects) ? "K" : "k", | |
374 | (message->flags & kSOSMessageDidClearGetObjectsSinceLastDelta) ? "Z" : "z", | |
375 | (message->flags & kSOSMessageSkipHello) ? "H" : "h"); | |
376 | } | |
377 | CFReleaseSafe(creationDate); | |
378 | CFReleaseSafe(objects); | |
379 | return desc; | |
380 | } | |
381 | ||
382 | // | |
383 | // MARK: SOSMessage encoding | |
384 | // | |
385 | ||
386 | // Create an SOSMessage ready to be encoded. | |
387 | SOSMessageRef SOSMessageCreate(CFAllocatorRef allocator, uint64_t version, CFErrorRef *error) { | |
388 | SOSMessageRef message = CFTypeAllocate(SOSMessage, struct __OpaqueSOSMessage, allocator); | |
389 | message->version = version; | |
390 | return message; | |
391 | } | |
392 | ||
393 | // TODO: Remove me this is for testing only, tests should use the real thing. | |
394 | SOSMessageRef SOSMessageCreateWithManifests(CFAllocatorRef allocator, SOSManifestRef sender, | |
395 | SOSManifestRef base, SOSManifestRef proposed, | |
396 | bool includeManifestDeltas, CFErrorRef *error) { | |
397 | SOSMessageRef message = SOSMessageCreate(allocator, kEngineMessageProtocolVersion, error); | |
398 | if (!SOSMessageSetManifests(message, sender, base, proposed, includeManifestDeltas, NULL, error)) | |
399 | CFReleaseNull(message); | |
400 | return message; | |
401 | } | |
402 | ||
403 | bool SOSMessageSetManifests(SOSMessageRef message, SOSManifestRef sender, | |
404 | SOSManifestRef base, SOSManifestRef proposed, | |
405 | bool includeManifestDeltas, SOSManifestRef objectsSent, | |
406 | CFErrorRef *error) { | |
407 | if (!message) return true; | |
408 | bool ok = true; | |
409 | // TODO: Check at v2 encoding time | |
410 | // if (!sender) return (SOSMessageRef)SOSErrorCreate(kSOSErrorProcessingFailure, error, NULL, CFSTR("no sender manifest specified for SOSMessage")); | |
411 | message->baseDigest = CFRetainSafe(SOSManifestGetDigest(base, NULL)); | |
412 | message->proposedDigest = CFRetainSafe(SOSManifestGetDigest(proposed, NULL)); | |
413 | message->senderDigest = CFRetainSafe(SOSManifestGetDigest(sender, NULL)); | |
414 | if (includeManifestDeltas) { | |
415 | SOSManifestRef additions = NULL; | |
416 | ok = SOSManifestDiff(base, proposed, &message->removals, &additions, error); | |
417 | if (message->version == 0) { | |
418 | message->additions = additions; | |
419 | } else { | |
420 | message->additions = SOSManifestCreateComplement(objectsSent, additions, error); | |
421 | CFReleaseSafe(additions); | |
422 | } | |
423 | } | |
424 | return ok; | |
425 | } | |
426 | ||
427 | void SOSMessageSetFlags(SOSMessageRef message, SOSMessageFlags flags) { | |
428 | message->flags = flags; | |
429 | } | |
430 | ||
431 | // Add an extension to this message | |
432 | void SOSMessageAddExtension(SOSMessageRef message, CFDataRef oid, bool isCritical, CFDataRef extension) { | |
433 | // TODO: Implement | |
434 | secerror("not implemented yet!"); | |
435 | } | |
436 | ||
437 | static bool SecMessageIsObjectValid(CFDataRef object, CFErrorRef *error) { | |
438 | const uint8_t *der = CFDataGetBytePtr(object); | |
439 | const uint8_t *der_end = der + CFDataGetLength(object); | |
440 | ccder_tag tag = 0; | |
441 | size_t len = 0; | |
442 | der = ccder_decode_tag(&tag, der, der_end); | |
443 | if (!der ) | |
444 | return SOSErrorCreate(kSOSErrorBadFormat, error, NULL, CFSTR("Invalid DER, no tag found")); | |
445 | if (tag == CCDER_EOL) | |
446 | return SOSErrorCreate(kSOSErrorBadFormat, error, NULL, CFSTR("Object has EOL tag")); | |
447 | der = ccder_decode_len(&len, der, der_end); | |
448 | if (!der) | |
449 | return SOSErrorCreate(kSOSErrorBadFormat, error, NULL, CFSTR("Object with tag %lu has no valid DER length"), tag); | |
450 | der += len; | |
451 | if (der_end - der) | |
452 | return SOSErrorCreate(kSOSErrorBadFormat, error, NULL, CFSTR("Object has %td trailing unused bytes"), der_end - der); | |
453 | return true; | |
454 | } | |
455 | ||
456 | bool SOSMessageAppendObject(SOSMessageRef message, CFDataRef object, CFErrorRef *error) { | |
457 | if (!SecMessageIsObjectValid(object, error)) return false; | |
458 | if (!message->objects) | |
459 | message->objects = CFArrayCreateMutableForCFTypes(CFGetAllocator(message)); | |
460 | if (message->objects) | |
461 | CFArrayAppendValue(message->objects, object); | |
462 | return true; | |
463 | } | |
464 | ||
465 | static CC_NONNULL_ALL | |
466 | size_t ccder_sizeof_bit_string(cc_size n, const cc_unit *s) { | |
467 | return ccder_sizeof(CCDER_BIT_STRING, ccn_sizeof(ccn_bitlen(n, s)) + 1); | |
468 | } | |
469 | ||
470 | static CC_NONNULL_ALL | |
471 | uint8_t *ccder_encode_bit_string(cc_size n, const cc_unit *s, const uint8_t *der, uint8_t *der_end) { | |
472 | size_t bits = ccn_bitlen(n, s); | |
473 | size_t out_size = ccn_sizeof(bits) + 1; | |
474 | der_end = ccder_encode_body_nocopy(out_size, der, der_end); | |
475 | if (der_end) | |
476 | ccn_write_uint_padded(n, s, out_size, der_end); | |
477 | return ccder_encode_tl(CCDER_BIT_STRING, out_size, der, der_end); | |
478 | } | |
479 | ||
480 | ||
5c19dc3a | 481 | static |
d8f41ccd A |
482 | size_t der_sizeof_implicit_data(ccder_tag tag, CFDataRef data) { |
483 | if (!data) | |
484 | return 0; | |
485 | return ccder_sizeof_implicit_raw_octet_string(tag, CFDataGetLength(data)); | |
486 | } | |
487 | ||
488 | ||
5c19dc3a | 489 | static CC_NONNULL((3, 4)) |
d8f41ccd A |
490 | uint8_t *der_encode_implicit_data(ccder_tag tag, CFDataRef data, const uint8_t *der, uint8_t *der_end) { |
491 | if (!data) | |
492 | return der_end; | |
493 | return ccder_encode_implicit_raw_octet_string(tag, CFDataGetLength(data), CFDataGetBytePtr(data), der, der_end); | |
494 | } | |
495 | ||
496 | static size_t der_sizeof_message_header(SOSMessageRef message, CFErrorRef *error) { | |
497 | if (!message->senderDigest) { | |
498 | // TODO: Create Error. | |
499 | return 0; | |
500 | } | |
501 | cc_unit flags[1]; | |
502 | flags[0] = (cc_unit)message->flags; // TODO Fix cast or something | |
503 | ||
504 | return ccder_sizeof(CCDER_CONSTRUCTED_SEQUENCE, | |
505 | der_sizeof_generalizedtime(message->creationTime, error) + | |
506 | ccder_sizeof_uint64(message->sequenceNumber) + | |
507 | ccder_sizeof_bit_string(array_size(flags), flags) + | |
508 | der_sizeof_implicit_data(CCDER_OCTET_STRING, message->senderDigest) + | |
509 | der_sizeof_implicit_data(0 | CCDER_CONTEXT_SPECIFIC, message->baseDigest) + | |
510 | der_sizeof_implicit_data(1 | CCDER_CONTEXT_SPECIFIC, message->proposedDigest)); | |
511 | } | |
512 | ||
513 | static uint8_t *der_encode_message_header(SOSMessageRef message, CFErrorRef *error, const uint8_t *der, uint8_t *der_end) { | |
514 | if (!message->senderDigest) { | |
515 | // TODO: Create Error. | |
516 | return NULL; | |
517 | } | |
518 | cc_unit flags[1]; | |
519 | flags[0] = (cc_unit)message->flags; // TODO Fix cast or something | |
520 | return ccder_encode_constructed_tl(CCDER_CONSTRUCTED_SEQUENCE, der_end, der, | |
521 | der_encode_generalizedtime(message->creationTime, error, der, | |
522 | ccder_encode_uint64(message->sequenceNumber, der, | |
523 | ccder_encode_bit_string(array_size(flags), flags, der, | |
524 | der_encode_implicit_data(CCDER_OCTET_STRING, message->senderDigest, der, | |
525 | der_encode_implicit_data(0 | CCDER_CONTEXT_SPECIFIC, message->baseDigest, der, | |
526 | der_encode_implicit_data(1 | CCDER_CONTEXT_SPECIFIC, message->proposedDigest, der, der_end))))))); | |
527 | } | |
528 | ||
529 | static size_t der_sizeof_deltas(SOSMessageRef message) { | |
530 | if (!message->additions && !message->removals) return 0; | |
531 | if (message->version == 0) { | |
532 | return ccder_sizeof(CCDER_OCTET_STRING, SOSManifestGetSize(message->removals))+ | |
533 | ccder_sizeof(CCDER_OCTET_STRING, SOSManifestGetSize(message->additions)); | |
534 | } else { | |
535 | return ccder_sizeof(0 | CCDER_CONTEXT_SPECIFIC | CCDER_CONSTRUCTED, | |
536 | ccder_sizeof(CCDER_OCTET_STRING, SOSManifestGetSize(message->removals))+ | |
537 | ccder_sizeof(CCDER_OCTET_STRING, SOSManifestGetSize(message->additions))); | |
538 | } | |
539 | } | |
540 | ||
541 | static uint8_t *der_encode_deltas(SOSMessageRef message, CFErrorRef *error, const uint8_t *der, uint8_t *der_end) { | |
542 | if (!message->additions && !message->removals) return der_end; | |
543 | if (message->version == 0) { | |
544 | return der_encode_implicit_data(CCDER_OCTET_STRING, SOSManifestGetData(message->removals), der, | |
545 | der_encode_implicit_data(CCDER_OCTET_STRING, SOSManifestGetData(message->additions), der, der_end)); | |
546 | } else { | |
547 | return ccder_encode_constructed_tl(0 | CCDER_CONTEXT_SPECIFIC | CCDER_CONSTRUCTED, der_end, der, | |
548 | der_encode_implicit_data(CCDER_OCTET_STRING, SOSManifestGetData(message->removals), der, | |
549 | der_encode_implicit_data(CCDER_OCTET_STRING, SOSManifestGetData(message->additions), der, der_end))); | |
550 | } | |
551 | } | |
552 | ||
553 | static size_t der_sizeof_extensions(SOSMessageRef message) { | |
554 | // We don't support any yet. | |
555 | return 0; | |
556 | } | |
557 | ||
558 | static uint8_t *der_encode_extensions(SOSMessageRef message, CFErrorRef *error, const uint8_t *der, uint8_t *der_end) { | |
559 | // We don't support any yet. | |
560 | return der_end; | |
561 | } | |
562 | ||
563 | static size_t der_sizeof_objects(SOSMessageRef message) { | |
564 | size_t len = 0; | |
565 | if (message->objects) { | |
566 | CFDataRef data; | |
567 | CFArrayForEachC(message->objects, data) { | |
568 | len += (size_t)CFDataGetLength(data); | |
569 | } | |
570 | } else if (message->version != 0) | |
571 | return 0; | |
572 | ||
573 | if (message->indefiniteLength) | |
574 | return len + 4; | |
575 | else | |
576 | return ccder_sizeof(2 | CCDER_CONTEXT_SPECIFIC | CCDER_CONSTRUCTED, len); | |
577 | } | |
578 | ||
579 | static uint8_t *der_encode_objects(SOSMessageRef message, CFErrorRef *error, const uint8_t *der, uint8_t *der_end) { | |
580 | if (!message->objects && message->version != 0) return der_end; | |
581 | const uint8_t *original_der_end = der_end; | |
582 | if (message->indefiniteLength) | |
583 | der_end = ccder_encode_tl(CCDER_EOL, 0, der, der_end); | |
584 | ||
585 | for (CFIndex position = (message->objects ? CFArrayGetCount(message->objects) : 0) - 1; position >= 0; --position) { | |
586 | CFDataRef object = CFArrayGetValueAtIndex(message->objects, position); | |
587 | der_end = ccder_encode_body(CFDataGetLength(object), CFDataGetBytePtr(object), der, der_end); | |
588 | } | |
589 | if (message->indefiniteLength) { | |
590 | return ccder_encode_tag(2 | CCDER_CONTEXT_SPECIFIC | CCDER_CONSTRUCTED, der, | |
591 | ccder_encode_len(0, der, der_end)); | |
592 | } else { | |
593 | ccder_tag otag = message->version == 0 ? CCDER_CONSTRUCTED_SEQUENCE : 2 | CCDER_CONTEXT_SPECIFIC | CCDER_CONSTRUCTED; | |
594 | return ccder_encode_constructed_tl(otag, original_der_end, der, der_end); | |
595 | } | |
596 | } | |
597 | ||
598 | static size_t der_sizeof_v2_message(SOSMessageRef message, CFErrorRef *error) { | |
599 | size_t body_size = (der_sizeof_message_header(message, error) + | |
600 | der_sizeof_deltas(message) + | |
601 | der_sizeof_extensions(message) + | |
602 | der_sizeof_objects(message)); | |
603 | if (message->indefiniteLength) | |
604 | return body_size + 4; | |
605 | else | |
606 | return ccder_sizeof(CCDER_CONSTRUCTED_SEQUENCE, body_size); | |
607 | } | |
608 | ||
609 | ||
610 | static uint8_t *der_encode_v2_message(SOSMessageRef message, CFErrorRef *error, const uint8_t *der, uint8_t *der_end) { | |
611 | const uint8_t *original_der_end = der_end; | |
612 | if (message->indefiniteLength) | |
613 | der_end = ccder_encode_tl(CCDER_EOL, 0, der, der_end); | |
614 | ||
615 | der_end = der_encode_message_header(message, error, der, | |
616 | der_encode_deltas(message, error, der, | |
617 | der_encode_extensions(message, error, der, | |
618 | der_encode_objects(message, error, der, der_end)))); | |
619 | ||
620 | if (message->indefiniteLength) { | |
621 | return ccder_encode_tag(CCDER_CONSTRUCTED_SEQUENCE, der, | |
622 | ccder_encode_len(0, der, der_end)); | |
623 | } else { | |
624 | return ccder_encode_constructed_tl(CCDER_CONSTRUCTED_SEQUENCE, original_der_end, der, der_end); | |
625 | } | |
626 | } | |
627 | ||
628 | //------------------------------------------------------------------------------------------------------------------------------------ | |
629 | // V1 support | |
630 | //------------------------------------------------------------------------------------------------------------------------------------ | |
631 | ||
632 | /* ManifestDigest message */ | |
633 | static size_t der_sizeof_manifest_digest_message(SOSMessageRef message, CFErrorRef *error) { | |
634 | if (!message->senderDigest || CFDataGetLength(message->senderDigest) != SOSDigestSize) { | |
635 | SOSErrorCreate(kSOSErrorProcessingFailure, error, NULL, CFSTR("digest length mismatch")); | |
636 | return 0; | |
637 | } | |
638 | return ccder_sizeof(CCDER_CONSTRUCTED_SEQUENCE, | |
639 | (ccder_sizeof_uint64(SOSManifestDigestMessageType) + | |
640 | ccder_sizeof_raw_octet_string(SOSDigestSize))); | |
641 | } | |
642 | ||
643 | static uint8_t *der_encode_manifest_digest_message(SOSMessageRef message, CFErrorRef *error, const uint8_t *der, uint8_t *der_end) { | |
644 | return ccder_encode_constructed_tl(CCDER_CONSTRUCTED_SEQUENCE, der_end, der, | |
645 | ccder_encode_uint64(SOSManifestDigestMessageType, der, | |
646 | ccder_encode_raw_octet_string(SOSDigestSize, CFDataGetBytePtr(message->senderDigest), der, der_end))); | |
647 | } | |
648 | ||
649 | /* Manifest message */ | |
650 | static size_t der_sizeof_manifest_message(SOSMessageRef message, CFErrorRef *error) { | |
651 | if (!message->additions) { | |
652 | SOSErrorCreate(kSOSErrorProcessingFailure, error, NULL, CFSTR("no manifest for manifest message")); | |
653 | return 0; | |
654 | } | |
655 | return ccder_sizeof(CCDER_CONSTRUCTED_SEQUENCE, | |
656 | (ccder_sizeof_uint64(SOSManifestMessageType) + | |
657 | der_sizeof_implicit_data(CCDER_OCTET_STRING, SOSManifestGetData(message->additions)))); | |
658 | } | |
659 | ||
660 | static uint8_t *der_encode_manifest_message(SOSMessageRef message, CFErrorRef *error, const uint8_t *der, uint8_t *der_end) { | |
661 | return ccder_encode_constructed_tl(CCDER_CONSTRUCTED_SEQUENCE, der_end, der, | |
662 | ccder_encode_uint64(SOSManifestMessageType, der, | |
663 | der_encode_implicit_data(CCDER_OCTET_STRING, SOSManifestGetData(message->additions), der, der_end))); | |
664 | } | |
665 | ||
666 | /* ManifestDeltaAndObjects message */ | |
667 | static size_t der_sizeof_manifest_and_objects_message(SOSMessageRef message, CFErrorRef *error) { | |
668 | if (!message->baseDigest || CFDataGetLength(message->baseDigest) != SOSDigestSize) { | |
669 | SOSErrorCreate(kSOSErrorProcessingFailure, error, NULL, CFSTR("digest length mismatch")); | |
670 | return 0; | |
671 | } | |
672 | ||
673 | return ccder_sizeof(CCDER_CONSTRUCTED_SEQUENCE, | |
674 | (ccder_sizeof_uint64(SOSManifestDeltaAndObjectsMessageType) + | |
675 | ccder_sizeof(CCDER_CONSTRUCTED_SEQUENCE, | |
676 | (ccder_sizeof_raw_octet_string(SOSDigestSize) + | |
677 | der_sizeof_implicit_data(CCDER_OCTET_STRING, SOSManifestGetData(message->removals)) + | |
678 | der_sizeof_implicit_data(CCDER_OCTET_STRING, SOSManifestGetData(message->additions)) + | |
679 | der_sizeof_objects(message))))); | |
680 | } | |
681 | ||
682 | static uint8_t *der_encode_manifest_and_objects_message(SOSMessageRef message, CFErrorRef *error, const uint8_t *der, uint8_t *der_end) { | |
683 | return ccder_encode_constructed_tl(CCDER_CONSTRUCTED_SEQUENCE, der_end, der, | |
684 | ccder_encode_uint64(SOSManifestDeltaAndObjectsMessageType, der, | |
685 | ccder_encode_constructed_tl(CCDER_CONSTRUCTED_SEQUENCE, der_end, der, | |
686 | ccder_encode_raw_octet_string(SOSDigestSize, CFDataGetBytePtr(message->baseDigest), der, | |
687 | der_encode_implicit_data(CCDER_OCTET_STRING, SOSManifestGetData(message->removals), der, | |
688 | der_encode_implicit_data(CCDER_OCTET_STRING, SOSManifestGetData(message->additions), der, | |
689 | der_encode_objects(message, error, der, der_end))))))); | |
690 | } | |
691 | ||
692 | static uint64_t SOSMessageInferType(SOSMessageRef message, CFErrorRef *error) { | |
693 | if (message->baseDigest) { | |
694 | // TODO: Assert that we don't have senderDigest or proposedDigest | |
695 | if (SOSManifestGetCount(message->removals) || SOSManifestGetCount(message->additions) || SOSMessageCountObjects(message)) { | |
696 | return SOSManifestDeltaAndObjectsMessageType; | |
697 | } else { | |
698 | // NOTE: If we force a SOSManifestDeltaAndObjectsMessageType instead then | |
699 | // true v0 peers will overwrite their last objects message to us. However this | |
700 | // implements the current v0 behaviour | |
701 | return SOSManifestDigestMessageType; | |
702 | } | |
703 | } else if (message->additions) { | |
704 | // TODO: Assert that we don't have senderDigest, proposedDigest, additions, removals or objects | |
705 | return SOSManifestMessageType; | |
706 | } else if (message->senderDigest) { | |
707 | // TODO: Assert that we don't have proposedDigest, removals or objects | |
708 | return SOSManifestDigestMessageType; | |
709 | } | |
710 | // TODO: Create error. | |
711 | return SOSManifestInvalidMessageType; | |
712 | } | |
713 | ||
714 | static size_t der_sizeof_message(SOSMessageRef message, uint64_t messageType, CFErrorRef *error) { | |
715 | switch (messageType) { | |
716 | case SOSManifestInvalidMessageType: | |
717 | return der_sizeof_v2_message(message, error); | |
718 | case SOSManifestDigestMessageType: | |
719 | return der_sizeof_manifest_digest_message(message, error); | |
720 | case SOSManifestMessageType: | |
721 | return der_sizeof_manifest_message(message, error); | |
722 | case SOSManifestDeltaAndObjectsMessageType: | |
723 | return der_sizeof_manifest_and_objects_message(message, error); | |
724 | } | |
725 | return 0; | |
726 | } | |
727 | ||
728 | static uint8_t *der_encode_message(SOSMessageRef message, uint64_t messageType, CFErrorRef *error, const uint8_t *der, uint8_t *der_end) { | |
729 | switch (messageType) { | |
730 | case SOSManifestInvalidMessageType: | |
731 | return der_encode_v2_message(message, error, der, der_end); | |
732 | case SOSManifestDigestMessageType: | |
733 | return der_encode_manifest_digest_message(message, error, der, der_end); | |
734 | case SOSManifestMessageType: | |
735 | return der_encode_manifest_message(message, error, der, der_end); | |
736 | case SOSManifestDeltaAndObjectsMessageType: | |
737 | return der_encode_manifest_and_objects_message(message, error, der, der_end); | |
738 | } | |
739 | return der_end; | |
740 | } | |
741 | ||
742 | // Encode an SOSMessage, calls addObject callback and appends returned objects | |
743 | // one by one, until addObject returns NULL. | |
744 | CFDataRef SOSMessageCreateData(SOSMessageRef message, uint64_t sequenceNumber, CFErrorRef *error) { | |
745 | // Version 2 message have sequence numbers, version 0 messages do not. | |
746 | uint64_t messageType = SOSManifestInvalidMessageType; | |
747 | message->sequenceNumber = sequenceNumber; | |
748 | if (message->version == 0) { | |
749 | message->indefiniteLength = false; | |
750 | messageType = SOSMessageInferType(message, error); | |
751 | if (!messageType) { | |
752 | // Propagate error | |
753 | return NULL; | |
754 | } | |
755 | } else { | |
756 | message->creationTime = floor(CFAbsoluteTimeGetCurrent()); | |
757 | } | |
758 | size_t der_size = der_sizeof_message(message, messageType, error); | |
759 | CFMutableDataRef data = CFDataCreateMutable(NULL, der_size); | |
760 | if (data == NULL) { | |
761 | // TODO Error. | |
762 | return NULL; | |
763 | } | |
764 | CFDataSetLength(data, der_size); | |
765 | uint8_t *der_end = CFDataGetMutableBytePtr(data); | |
766 | const uint8_t *der = der_end; | |
767 | der_end += der_size; | |
768 | ||
769 | der_end = der_encode_message(message, messageType, error, der, der_end); | |
770 | if (der != der_end) { | |
771 | secwarning("internal error %td bytes unused in der buffer", der_end - der); | |
772 | } | |
773 | return data; | |
774 | } | |
775 | ||
776 | // | |
777 | // MARK: SOSMessage decoding | |
778 | // | |
779 | ||
780 | #define CCBER_LEN_INDEFINITE ((size_t)-1) | |
781 | ||
782 | // Decode BER length field. Sets *lenp to ccber_indefinite_len if this is an indefinite length encoded object. | |
783 | // Behaves like ccder_decode_len in every other way. | |
784 | static CC_NONNULL((1, 3)) | |
785 | const uint8_t *ccber_decode_len(size_t *lenp, const uint8_t *der, const uint8_t *der_end) { | |
786 | if (der && der < der_end) { | |
787 | if (*der == 0x80) { | |
788 | der++; | |
789 | *lenp = CCBER_LEN_INDEFINITE; | |
790 | } | |
791 | else | |
792 | der = ccder_decode_len(lenp, der, der_end); | |
793 | } | |
794 | return der; | |
795 | } | |
796 | ||
797 | static const uint8_t *der_decode_generalizedtime(CFAbsoluteTime *at, CFErrorRef *error, const uint8_t *der, const uint8_t *der_end) { | |
798 | const uint8_t *times_end = NULL; | |
799 | der = ccder_decode_constructed_tl(CCDER_GENERALIZED_TIME, ×_end, der, der_end); | |
800 | der = der_decode_generalizedtime_body(at, error, der, times_end); | |
801 | if (times_end != der) { | |
802 | secwarning("internal error %td bytes unused in generalizedtime DER buffer", times_end - der); | |
803 | } | |
804 | return der; | |
805 | } | |
806 | ||
807 | static const uint8_t *der_decode_optional_generalizedtime(CFAbsoluteTime *at, CFErrorRef *error, const uint8_t *der, const uint8_t *der_end) { | |
808 | const uint8_t *times_end = der_decode_generalizedtime(at, error, der, der_end); | |
809 | return times_end ? times_end : der; | |
810 | } | |
811 | ||
812 | static CC_NONNULL((2, 4)) | |
813 | const uint8_t *ccder_decode_implicit_uint64(ccder_tag expected_tag, uint64_t* r, const uint8_t *der, const uint8_t *der_end) { | |
814 | size_t len; | |
815 | der = ccder_decode_tl(expected_tag, &len, der, der_end); | |
816 | if (der && len && (*der & 0x80) != 0x80) { | |
5c19dc3a | 817 | if (ccn_read_uint(ccn_nof_size(sizeof(*r)), (cc_unit*)r, len, der) >= 0) |
d8f41ccd A |
818 | return der + len; |
819 | } | |
820 | return NULL; | |
821 | } | |
822 | ||
823 | static const uint8_t *ccder_decode_optional_implicit_uint64(ccder_tag expected_tag, uint64_t *value, const uint8_t *der, const uint8_t *der_end) { | |
824 | const uint8_t *ui64_end = ccder_decode_implicit_uint64(expected_tag, value, der, der_end); | |
825 | return ui64_end ? ui64_end : der; | |
826 | } | |
827 | ||
828 | ||
829 | static const uint8_t *ccder_decode_optional_uint64(uint64_t *value, const uint8_t *der, const uint8_t *der_end) { | |
830 | const uint8_t *ui64_end = ccder_decode_uint64(value, der, der_end); | |
831 | return ui64_end ? ui64_end : der; | |
832 | } | |
833 | ||
834 | static const uint8_t *ccder_decode_digest_types(SOSMessageRef message, const uint8_t *der, const uint8_t *der_end) { | |
835 | const uint8_t *dt_end; | |
836 | der = ccder_decode_sequence_tl(&dt_end, der, der_end); | |
837 | if (!der) return NULL; | |
838 | // Skip over digestType body for now. | |
839 | // TODO: Support DigestType | |
840 | return dt_end; | |
841 | } | |
842 | ||
843 | static const uint8_t *ccder_decode_optional_digest_types(SOSMessageRef message, const uint8_t *der, const uint8_t *der_end) { | |
844 | const uint8_t *dt_end = ccder_decode_digest_types(message, der, der_end); | |
845 | return dt_end ? dt_end : der; | |
846 | } | |
847 | ||
848 | static const uint8_t *ccder_decode_bit_string(cc_size n, size_t *r_bitlen, cc_unit *r, const uint8_t *der, const uint8_t *der_end) { | |
849 | size_t len; | |
850 | const uint8_t *body = ccder_decode_tl(CCDER_BIT_STRING, &len, der, der_end); | |
851 | if (!body || len < 1) | |
852 | return NULL; | |
853 | ||
854 | if (r_bitlen) *r_bitlen = (len - 1) * 8 - (body[0] & 7); | |
855 | ccn_read_uint(1, r, len - 1, body + 1); | |
856 | return body + len; | |
857 | } | |
858 | ||
859 | static const uint8_t *der_decode_implicit_data(ccder_tag expected_tag, CFDataRef *data, const uint8_t *der, const uint8_t *der_end) { | |
860 | size_t len = 0; | |
861 | der = ccder_decode_tl(expected_tag, &len, der, der_end); | |
862 | if (der && data) { | |
863 | *data = CFDataCreateWithBytesNoCopy(kCFAllocatorDefault, der, len, kCFAllocatorNull); | |
864 | if (*data) | |
865 | der += len; | |
866 | else | |
867 | der = NULL; | |
868 | } | |
869 | return der; | |
870 | } | |
871 | ||
872 | static const uint8_t *der_decode_optional_implicit_data(ccder_tag expected_tag, CFDataRef *data, const uint8_t *der, const uint8_t *der_end) { | |
873 | const uint8_t *data_end = der_decode_implicit_data(expected_tag, data, der, der_end); | |
874 | return data_end ? data_end : der; | |
875 | } | |
876 | ||
877 | static const uint8_t *der_decode_deltas_body(SOSMessageRef message, CFErrorRef *error, const uint8_t *der, const uint8_t *der_end) { | |
878 | CFDataRef removals = NULL, additions = NULL; | |
879 | der = der_decode_implicit_data(CCDER_OCTET_STRING, &removals, der, der_end); | |
880 | der = der_decode_implicit_data(CCDER_OCTET_STRING, &additions, der, der_end); | |
881 | if (der) { | |
882 | message->removals = SOSManifestCreateWithData(removals, error); | |
883 | message->additions = SOSManifestCreateWithData(additions, error); | |
884 | if (!message->removals || !message->additions) { | |
885 | CFReleaseNull(message->removals); | |
886 | CFReleaseNull(message->additions); | |
887 | der = NULL; | |
888 | } | |
889 | } | |
890 | CFReleaseSafe(removals); | |
891 | CFReleaseSafe(additions); | |
892 | ||
893 | return der; | |
894 | } | |
895 | ||
896 | static const uint8_t *der_decode_deltas(SOSMessageRef message, CFErrorRef *error, const uint8_t *der, const uint8_t *der_end) { | |
897 | const uint8_t *deltas_end = NULL; | |
898 | der = ccder_decode_constructed_tl(0 | CCDER_CONTEXT_SPECIFIC | CCDER_CONSTRUCTED, &deltas_end, der, der_end); | |
899 | return der_decode_deltas_body(message, error, der, deltas_end); | |
900 | } | |
901 | ||
902 | static const uint8_t *der_decode_optional_deltas(SOSMessageRef message, const uint8_t *der, const uint8_t *der_end) { | |
903 | const uint8_t *seq_end = der_decode_deltas(message, NULL, der, der_end); | |
904 | return seq_end ? seq_end : der; | |
905 | } | |
906 | ||
907 | static const uint8_t *der_decode_extensions(SOSMessageRef message, CFErrorRef *error, const uint8_t *der, const uint8_t *der_end) { | |
908 | const uint8_t *extensions_end; | |
909 | der = ccder_decode_constructed_tl(1 | CCDER_CONTEXT_SPECIFIC | CCDER_CONSTRUCTED, &extensions_end, der, der_end); | |
910 | if (!der) return NULL; | |
911 | // Skip over extensions for now. | |
912 | return extensions_end; | |
913 | } | |
914 | ||
915 | static const uint8_t *der_decode_optional_extensions(SOSMessageRef message, CFErrorRef *error, const uint8_t *der, const uint8_t *der_end) { | |
916 | const uint8_t *extensions_end = der_decode_extensions(message, NULL, der, der_end); | |
917 | return extensions_end ? extensions_end : der; | |
918 | } | |
919 | ||
920 | static const uint8_t *der_foreach_objects(size_t length, const uint8_t *der, const uint8_t *der_end, CFErrorRef *error, void(^withObject)(CFDataRef object, bool *stop)) { | |
921 | bool stop = false; | |
922 | ccder_tag tag; | |
923 | // Look ahead at the tag | |
924 | while (!stop && ccder_decode_tag(&tag, der, der_end) && tag != CCDER_EOL) { | |
925 | const uint8_t *object_end = NULL; | |
926 | if (!ccder_decode_constructed_tl(tag, &object_end, der, der_end)) { | |
927 | SOSErrorCreate(kSOSErrorDecodeFailure, error, NULL, CFSTR("failed to decode object header")); | |
928 | return NULL; | |
929 | } | |
930 | if (withObject) { | |
931 | CFDataRef object = CFDataCreateWithBytesNoCopy(kCFAllocatorDefault, der, object_end - der, kCFAllocatorNull); | |
932 | withObject(object, &stop); | |
933 | CFReleaseSafe(object); | |
934 | } | |
935 | der = object_end; | |
936 | } | |
937 | if (length == CCBER_LEN_INDEFINITE) { | |
938 | size_t len = 0; | |
939 | der = ccder_decode_tl(CCDER_EOL, &len, der, der_end); | |
940 | if (len != 0) { | |
941 | secwarning("%td length ", der_end - der); | |
942 | } | |
943 | } | |
944 | if (!stop && der != der_end) | |
945 | secwarning("%td trailing bytes after objects DER", der_end - der); | |
946 | ||
947 | return der; | |
948 | } | |
949 | ||
950 | static const uint8_t *der_decode_objects(SOSMessageRef message, CFErrorRef *error, const uint8_t *der, const uint8_t *der_end) { | |
951 | ccder_tag tag = 0; | |
952 | size_t objects_len = 0; | |
953 | der = ccder_decode_tag(&tag, der, der_end); | |
954 | if (tag != (2 | CCDER_CONTEXT_SPECIFIC | CCDER_CONSTRUCTED)) return NULL; | |
955 | der = ccber_decode_len(&objects_len, der, der_end); | |
956 | if (objects_len != CCBER_LEN_INDEFINITE && der_end - der != (ptrdiff_t)objects_len) { | |
957 | secwarning("%td trailing bytes after SOSMessage DER", (der_end - der) - (ptrdiff_t)objects_len); | |
958 | } | |
959 | // Remember a pointer into message->der where objects starts. | |
960 | message->objectsDer = der; | |
961 | message->objectsLen = objects_len; | |
962 | ||
963 | return der + objects_len; | |
964 | } | |
965 | ||
966 | static const uint8_t *der_decode_optional_objects(SOSMessageRef message, CFErrorRef *error, const uint8_t *der, const uint8_t *der_end) { | |
967 | const uint8_t *seq_end = der_decode_objects(message, NULL, der, der_end); | |
968 | return seq_end ? seq_end : der; | |
969 | } | |
970 | ||
971 | #if 0 | |
972 | // Move to ccder and possibly refactor ccder_decode_constructed_tl to call this. | |
973 | #ifdef CCDER_DECODE_CONSTRUCTED_LEN_SPECIFIER | |
974 | CCDER_DECODE_CONSTRUCTED_LEN_SPECIFIER | |
975 | #endif | |
976 | inline CC_NONNULL((1, 3)) | |
977 | const uint8_t * | |
978 | ccder_decode_constructed_len(const uint8_t **body_end, | |
979 | const uint8_t *der, const uint8_t *der_end) { | |
980 | size_t len; | |
981 | der = ccder_decode_len(&len, der, der_end); | |
982 | *body_end = der + len; | |
983 | return der; | |
984 | } | |
985 | #endif | |
986 | ||
987 | static const uint8_t *der_decode_message_header(SOSMessageRef message, CFErrorRef *error, const uint8_t *der, const uint8_t *der_end) { | |
988 | cc_unit flags[1] = {}; | |
989 | der = ccder_decode_constructed_tl(CCDER_CONSTRUCTED_SEQUENCE, &der_end, der, der_end); | |
990 | message->version = 2; | |
991 | der = ccder_decode_optional_implicit_uint64(0 | CCDER_CONTEXT_SPECIFIC, &message->version, der, der_end); | |
992 | der = der_decode_optional_generalizedtime(&message->creationTime, error, der, der_end); | |
993 | der = ccder_decode_optional_uint64(&message->sequenceNumber, der, der_end); | |
994 | der = ccder_decode_optional_digest_types(message, der, der_end); | |
995 | der = ccder_decode_bit_string(array_size(flags), NULL, flags, der, der_end); | |
996 | message->flags = flags[0]; | |
997 | ||
998 | der = der_decode_implicit_data(CCDER_OCTET_STRING, &message->senderDigest, der, der_end); | |
999 | der = der_decode_optional_implicit_data(0 | CCDER_CONTEXT_SPECIFIC, &message->baseDigest, der, der_end); | |
1000 | der = der_decode_optional_implicit_data(1 | CCDER_CONTEXT_SPECIFIC, &message->proposedDigest, der, der_end); | |
1001 | return der; | |
1002 | } | |
1003 | ||
1004 | static const uint8_t * | |
1005 | der_decode_manifest_and_objects_message(SOSMessageRef message, | |
1006 | CFErrorRef *error, const uint8_t *der, | |
1007 | const uint8_t *der_end) { | |
1008 | size_t objects_len = 0; | |
1009 | const uint8_t *body_end; | |
1010 | der = ccder_decode_sequence_tl(&body_end, der, der_end); | |
1011 | if (body_end != der_end) { | |
5c19dc3a | 1012 | SOSErrorCreate(kSOSErrorInvalidMessage, error, NULL, CFSTR("Trailing garbage at end of message")); |
d8f41ccd A |
1013 | return NULL; |
1014 | } | |
1015 | der = der_decode_implicit_data(CCDER_OCTET_STRING, &message->baseDigest, der, body_end); | |
1016 | der = der_decode_deltas_body(message, error, der, body_end); | |
1017 | // Remember a pointer into message->der where objects starts. | |
1018 | der = message->objectsDer = ccder_decode_tl(CCDER_CONSTRUCTED_SEQUENCE, &objects_len, der, body_end); | |
1019 | message->objectsLen = objects_len; | |
1020 | ||
1021 | return der ? der + objects_len : NULL; | |
1022 | } | |
1023 | ||
1024 | static const uint8_t *der_decode_v0_message_body(SOSMessageRef message, CFErrorRef *error, const uint8_t *der, const uint8_t *der_end) { | |
1025 | uint64_t messageType = 0; | |
1026 | der = ccder_decode_uint64(&messageType, der, der_end); | |
1027 | if (der) switch (messageType) { | |
1028 | case SOSManifestDigestMessageType: | |
1029 | { | |
1030 | der = der_decode_implicit_data(CCDER_OCTET_STRING, &message->senderDigest, der, der_end); | |
1031 | break; | |
1032 | } | |
1033 | case SOSManifestMessageType: | |
1034 | { | |
1035 | CFDataRef manifestBody = NULL; | |
1036 | der = der_decode_implicit_data(CCDER_OCTET_STRING, &manifestBody, der, der_end); | |
1037 | if (!der) return NULL; | |
1038 | if (der != der_end) { | |
1039 | secwarning("%td trailing bytes after deltas DER", der_end - der); | |
1040 | } | |
1041 | message->additions = SOSManifestCreateWithData(manifestBody, error); | |
1042 | CFReleaseSafe(manifestBody); | |
1043 | break; | |
1044 | } | |
1045 | case SOSManifestDeltaAndObjectsMessageType: | |
1046 | { | |
1047 | der = der_decode_manifest_and_objects_message(message, error, der, der_end); | |
1048 | break; | |
1049 | } | |
1050 | default: | |
5c19dc3a | 1051 | SOSErrorCreate(kSOSErrorInvalidMessage, error, NULL, CFSTR("Invalid message type %llu"), messageType); |
d8f41ccd A |
1052 | break; |
1053 | } | |
1054 | return der; | |
1055 | } | |
1056 | ||
1057 | static const uint8_t *der_decode_message(SOSMessageRef message, CFErrorRef *error, const uint8_t *der, const uint8_t *der_end) { | |
1058 | ccder_tag tag = 0; | |
1059 | size_t body_len = 0; | |
1060 | ||
1061 | der = ccder_decode_tag(&tag, der, der_end); | |
1062 | if (tag != CCDER_CONSTRUCTED_SEQUENCE) return NULL; | |
1063 | der = ccber_decode_len(&body_len, der, der_end); | |
1064 | if (der && body_len && body_len != CCBER_LEN_INDEFINITE && (der_end - der) != (ptrdiff_t)body_len) { | |
1065 | secwarning("%td trailing bytes after SOSMessage DER", (der_end - der) - (ptrdiff_t)body_len); | |
1066 | der_end = der + body_len; | |
1067 | } | |
1068 | ||
1069 | if (ccder_decode_tag(&tag, der, der_end)) switch (tag) { | |
1070 | case CCDER_INTEGER: // v0 | |
1071 | if (body_len == CCBER_LEN_INDEFINITE) | |
1072 | der = NULL; // Not supported for v0 messages | |
1073 | else | |
1074 | der = der_decode_v0_message_body(message, error, der, der_end); | |
1075 | break; | |
1076 | case CCDER_CONSTRUCTED_SEQUENCE: //v2 | |
1077 | der = der_decode_message_header(message, error, der, der_end); | |
1078 | der = der_decode_optional_deltas(message, der, der_end); | |
1079 | der = der_decode_optional_extensions(message, error, der, der_end); | |
1080 | der = der_decode_optional_objects(message, error, der, der_end); | |
1081 | break; | |
1082 | } | |
1083 | return der; | |
1084 | } | |
1085 | ||
1086 | // Decode a SOSMessage | |
1087 | SOSMessageRef SOSMessageCreateWithData(CFAllocatorRef allocator, CFDataRef derData, CFErrorRef *error) { | |
1088 | if (!derData) | |
1089 | return (SOSMessageRef)SOSErrorCreate(kSOSErrorDecodeFailure, error, NULL, CFSTR("NULL data => no SOSMessage")); | |
1090 | SOSMessageRef message = CFTypeAllocate(SOSMessage, struct __OpaqueSOSMessage, allocator); | |
1091 | if (!message) | |
1092 | return (SOSMessageRef)SOSErrorCreate(kSOSErrorAllocationFailure, error, NULL, CFSTR("failed to alloc SOSMessage")); | |
1093 | message->der = CFRetainSafe(derData); | |
1094 | const uint8_t *der = CFDataGetBytePtr(derData); | |
1095 | const uint8_t *der_end = der + CFDataGetLength(derData); | |
1096 | der = der_decode_message(message, error, der, der_end); | |
5c19dc3a | 1097 | if (!der_end || der != der_end) { |
d8f41ccd A |
1098 | if (error && !*error) |
1099 | SOSErrorCreate(kSOSErrorDecodeFailure, error, NULL, CFSTR("SOSMessage DER decoding failure %td bytes left"), der_end - der); | |
1100 | return CFReleaseSafe(message); | |
1101 | } | |
1102 | return message; | |
1103 | } | |
1104 | ||
1105 | // Read values from a decoded messgage | |
1106 | ||
1107 | CFDataRef SOSMessageGetBaseDigest(SOSMessageRef message) { | |
1108 | return message->baseDigest; | |
1109 | } | |
1110 | ||
1111 | CFDataRef SOSMessageGetProposedDigest(SOSMessageRef message) { | |
1112 | return message->proposedDigest; | |
1113 | } | |
1114 | ||
1115 | CFDataRef SOSMessageGetSenderDigest(SOSMessageRef message) { | |
1116 | return message->senderDigest; | |
1117 | } | |
1118 | ||
1119 | SOSMessageFlags SOSMessageGetFlags(SOSMessageRef message) { | |
1120 | return message->flags; | |
1121 | } | |
1122 | ||
1123 | uint64_t SOSMessageGetSequenceNumber(SOSMessageRef message) { | |
1124 | return message->sequenceNumber; | |
1125 | } | |
1126 | ||
1127 | SOSManifestRef SOSMessageGetRemovals(SOSMessageRef message) { | |
1128 | return message->removals; | |
1129 | } | |
1130 | ||
1131 | SOSManifestRef SOSMessageGetAdditions(SOSMessageRef message) { | |
1132 | return message->additions; | |
1133 | } | |
1134 | ||
1135 | // Iterate though the extensions in a decoded SOSMessage. If criticalOnly is | |
1136 | // true all non critical extensions are skipped. | |
1137 | void SOSMessageWithExtensions(SOSMessageRef message, bool criticalOnly, void(^withExtension)(CFDataRef oid, bool isCritical, CFDataRef extension, bool *stop)) { | |
1138 | // TODO | |
1139 | } | |
1140 | ||
1141 | size_t SOSMessageCountObjects(SOSMessageRef message) { | |
1142 | if (message->objects) | |
1143 | return CFArrayGetCount(message->objects); | |
1144 | if (!message->objectsDer) | |
1145 | return 0; | |
1146 | const uint8_t *der = CFDataGetBytePtr(message->der); | |
1147 | const uint8_t *der_end = der + CFDataGetLength(message->der); | |
1148 | __block size_t count = 0; | |
1149 | der_foreach_objects(message->objectsLen, message->objectsDer, der_end, NULL, ^(CFDataRef object, bool *stop){ ++count; }); | |
1150 | return count; | |
1151 | } | |
1152 | ||
1153 | // Iterate though the objects in a decoded SOSMessage. | |
1154 | bool SOSMessageWithObjects(SOSMessageRef message, CFErrorRef *error, | |
1155 | void(^withObject)(CFDataRef object, bool *stop)) { | |
1156 | if (message->objects) { | |
1157 | CFDataRef object; | |
1158 | CFArrayForEachC(message->objects, object) { | |
1159 | bool stop = false; | |
1160 | withObject(object, &stop); | |
1161 | if (stop) | |
1162 | break; | |
1163 | } | |
1164 | return true; | |
1165 | } | |
1166 | if (!message->objectsDer) | |
1167 | return true; | |
1168 | const uint8_t *der = CFDataGetBytePtr(message->der); | |
1169 | const uint8_t *der_end = der + CFDataGetLength(message->der); | |
1170 | return der_foreach_objects(message->objectsLen, message->objectsDer, der_end, error, withObject); | |
1171 | } | |
1172 | ||
1173 | bool SOSMessageWithSOSObjects(SOSMessageRef message, SOSDataSourceRef dataSource, CFErrorRef *error, | |
1174 | void(^withObject)(SOSObjectRef object, bool *stop)) { | |
1175 | return SOSMessageWithObjects(message, error, ^(CFDataRef object, bool *stop) { | |
1176 | CFDictionaryRef plist = NULL; | |
1177 | const uint8_t *der = CFDataGetBytePtr(object); | |
1178 | const uint8_t *der_end = der + CFDataGetLength(object); | |
5c19dc3a | 1179 | // TODO Remove intermediate plist format |
d8f41ccd A |
1180 | der = der_decode_dictionary(kCFAllocatorDefault, kCFPropertyListImmutable, &plist, error, der, der_end); |
1181 | if (der) { | |
1182 | SOSObjectRef peersObject = SOSObjectCreateWithPropertyList(dataSource, plist, error); | |
1183 | withObject(peersObject, stop); | |
1184 | CFReleaseSafe(peersObject); | |
1185 | } | |
1186 | CFReleaseSafe(plist); | |
1187 | }); | |
1188 | } |