]>
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 | ||
b54c578e | 29 | #include "keychain/SecureObjectSync/SOSMessage.h" |
d8f41ccd A |
30 | |
31 | #include <AssertMacros.h> | |
32 | #include <CoreFoundation/CoreFoundation.h> | |
b54c578e A |
33 | #include "keychain/SecureObjectSync/SOSDigestVector.h" |
34 | #include "keychain/SecureObjectSync/SOSManifest.h" | |
35 | #include "keychain/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. | |
7fb2cbd2 | 51 | #include "keychain/securityd/SecItemDataSource.h" |
d8f41ccd A |
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 | ||
d8f41ccd A |
214 | // |
215 | // MARK: SOSMessage implementation. | |
216 | // | |
217 | ||
218 | // Legacy v1 message type numbers | |
219 | enum SOSMessageType { | |
220 | SOSManifestInvalidMessageType = 0, | |
221 | SOSManifestDigestMessageType = 1, | |
222 | SOSManifestMessageType = 2, | |
223 | SOSManifestDeltaAndObjectsMessageType = 3, | |
224 | }; | |
225 | ||
226 | struct __OpaqueSOSMessage { | |
227 | CFRuntimeBase _base; | |
228 | ||
229 | CFDataRef der; | |
230 | const uint8_t *objectsDer; | |
231 | size_t objectsLen; | |
232 | ||
233 | CFDataRef senderDigest; | |
234 | CFDataRef baseDigest; | |
235 | CFDataRef proposedDigest; | |
236 | SOSManifestRef removals; | |
237 | SOSManifestRef additions; | |
238 | ||
239 | CFMutableArrayRef objects; | |
240 | ||
241 | SOSMessageFlags flags; | |
242 | uint64_t sequenceNumber; | |
243 | CFAbsoluteTime creationTime; | |
244 | uint64_t version; // Message version (currently always 2) | |
245 | bool indefiniteLength; // If set to true the top SEQUENCE and the OBJECTS SEQUENCE are written indefinite length. | |
246 | }; | |
247 | ||
248 | CFGiblisWithCompareFor(SOSMessage) | |
249 | ||
250 | static Boolean SOSMessageCompare(CFTypeRef cf1, CFTypeRef cf2) { | |
251 | SOSMessageRef M = (SOSMessageRef)cf1; | |
252 | SOSMessageRef P = (SOSMessageRef)cf2; | |
253 | if (M->flags != P->flags) return false; | |
254 | if (M->sequenceNumber != P->sequenceNumber) return false; | |
255 | if (M->creationTime != P->creationTime) return false; | |
256 | //if (!CFEqualSafe(M->der, P->der)) return false; | |
257 | if (!CFEqualSafe(M->senderDigest, P->senderDigest)) return false; | |
258 | if (!CFEqualSafe(M->baseDigest, P->baseDigest)) return false; | |
259 | if (!CFEqualSafe(M->proposedDigest, P->proposedDigest)) return false; | |
260 | if (!CFEqualSafe(M->removals, P->removals)) return false; | |
261 | if (!CFEqualSafe(M->additions, P->additions)) return false; | |
262 | ||
263 | // TODO Compare Objects if present. | |
264 | ||
265 | return true; | |
266 | } | |
267 | ||
268 | static void SOSMessageDestroy(CFTypeRef cf) { | |
269 | SOSMessageRef message = (SOSMessageRef)cf; | |
270 | CFReleaseNull(message->der); | |
271 | CFReleaseNull(message->senderDigest); | |
272 | CFReleaseNull(message->baseDigest); | |
273 | CFReleaseNull(message->proposedDigest); | |
274 | CFReleaseNull(message->additions); | |
275 | CFReleaseNull(message->removals); | |
276 | CFReleaseNull(message->objects); | |
277 | } | |
278 | ||
279 | // TODO: Remove this layer violation! | |
7fb2cbd2 | 280 | #include "keychain/securityd/SecItemServer.h" |
d8f41ccd A |
281 | |
282 | static uint64_t SOSMessageInferType(SOSMessageRef message, CFErrorRef *error); | |
283 | ||
d87e1158 | 284 | static CFStringRef SOSMessageCopyFormatDescription(CFTypeRef cf, CFDictionaryRef formatOptions) { |
d8f41ccd A |
285 | SOSMessageRef message = (SOSMessageRef)cf; |
286 | static const uint8_t zero[4] = {}; | |
287 | const uint8_t *S = message->senderDigest ? CFDataGetBytePtr(message->senderDigest) : zero; | |
288 | const uint8_t *B = message->baseDigest ? CFDataGetBytePtr(message->baseDigest) : zero; | |
289 | const uint8_t *P = message->proposedDigest ? CFDataGetBytePtr(message->proposedDigest) : zero; | |
290 | CFDateRef creationDate = CFDateCreate(CFGetAllocator(message), message->creationTime); | |
291 | ||
292 | CFMutableStringRef objects = CFStringCreateMutable(kCFAllocatorDefault, 0); | |
293 | ||
294 | // TODO: Remove this layer violation! | |
295 | SOSDataSourceFactoryRef dsf = SecItemDataSourceFactoryGetDefault(); | |
5c19dc3a | 296 | SOSDataSourceRef ds = SOSDataSourceFactoryCreateDataSource(dsf, kSecAttrAccessibleWhenUnlocked, NULL); |
d8f41ccd | 297 | |
6b200bc3 A |
298 | if (ds) { |
299 | __block size_t maxEntries = 16; | |
300 | CFStringAppendFormat(objects, NULL, CFSTR("{[%zu]"), SOSMessageCountObjects(message)); | |
301 | SOSMessageWithSOSObjects(message, ds, NULL, ^(SOSObjectRef object, bool *stop) { | |
302 | CFDataRef digest = SOSObjectCopyDigest(ds, object, NULL); | |
303 | const uint8_t *O = CFDataGetBytePtr(digest); | |
304 | CFStringAppendFormat(objects, NULL, CFSTR(" %02X%02X%02X%02X"), O[0],O[1],O[2],O[3]); | |
305 | CFReleaseSafe(digest); | |
306 | if (!--maxEntries) { | |
307 | CFStringAppend(objects, CFSTR("...")); | |
308 | *stop = true; | |
309 | } | |
310 | }); | |
311 | CFStringAppend(objects, CFSTR("}")); | |
312 | } else { | |
313 | CFStringAppend(objects, CFSTR("{NO DATASOURCE}")); | |
314 | } | |
d8f41ccd | 315 | |
6b200bc3 | 316 | CFStringRef desc = NULL; |
d8f41ccd A |
317 | if (message->version == 0) { |
318 | switch (SOSMessageInferType(message, NULL)) { | |
319 | case SOSManifestInvalidMessageType: | |
320 | desc = CFStringCreateWithFormat(CFGetAllocator(message), NULL, CFSTR("<MSGInvalid %"PRIu64" >"), message->sequenceNumber); | |
321 | break; | |
322 | case SOSManifestDigestMessageType: | |
323 | desc = CFStringCreateWithFormat(CFGetAllocator(message), NULL, CFSTR("<MSGDigest %"PRIu64" %02X%02X%02X%02X>"), message->sequenceNumber, S[0],S[1],S[2],S[3]); | |
324 | break; | |
325 | case SOSManifestMessageType: | |
326 | desc = CFStringCreateWithFormat(CFGetAllocator(message), NULL, CFSTR("<MSGManifest %"PRIu64" %@>"), message->sequenceNumber, message->additions); | |
327 | break; | |
328 | case SOSManifestDeltaAndObjectsMessageType: | |
329 | desc = CFStringCreateWithFormat(CFGetAllocator(message), NULL, CFSTR("<MSGObjects %"PRIu64" %02X%02X%02X%02X %@ %@ %@"), | |
330 | message->sequenceNumber, | |
331 | B[0],B[1],B[2],B[3], | |
332 | message->removals, message->additions, | |
333 | objects); | |
334 | break; | |
335 | } | |
336 | } else { | |
337 | desc = CFStringCreateWithFormat | |
338 | (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>"), | |
339 | message->sequenceNumber, | |
340 | creationDate, | |
341 | S[0],S[1],S[2],S[3], | |
342 | B[0],B[1],B[2],B[3], | |
343 | P[0],P[1],P[2],P[3], | |
344 | message->removals, message->additions, | |
345 | objects, | |
346 | (message->flags & kSOSMessageGetObjects) ? "G" : "g", | |
347 | (message->flags & kSOSMessageJoinRequest) ? "J" : "j", | |
348 | (message->flags & kSOSMessagePartial) ? "P" : "p", | |
349 | (message->flags & kSOSMessageDigestTypesProposed) ? "D" : "d", | |
350 | (message->flags & kSOSMessageClearGetObjects) ? "K" : "k", | |
351 | (message->flags & kSOSMessageDidClearGetObjectsSinceLastDelta) ? "Z" : "z", | |
352 | (message->flags & kSOSMessageSkipHello) ? "H" : "h"); | |
353 | } | |
354 | CFReleaseSafe(creationDate); | |
355 | CFReleaseSafe(objects); | |
356 | return desc; | |
357 | } | |
358 | ||
359 | // | |
360 | // MARK: SOSMessage encoding | |
361 | // | |
362 | ||
363 | // Create an SOSMessage ready to be encoded. | |
364 | SOSMessageRef SOSMessageCreate(CFAllocatorRef allocator, uint64_t version, CFErrorRef *error) { | |
365 | SOSMessageRef message = CFTypeAllocate(SOSMessage, struct __OpaqueSOSMessage, allocator); | |
366 | message->version = version; | |
367 | return message; | |
368 | } | |
369 | ||
370 | // TODO: Remove me this is for testing only, tests should use the real thing. | |
371 | SOSMessageRef SOSMessageCreateWithManifests(CFAllocatorRef allocator, SOSManifestRef sender, | |
372 | SOSManifestRef base, SOSManifestRef proposed, | |
373 | bool includeManifestDeltas, CFErrorRef *error) { | |
374 | SOSMessageRef message = SOSMessageCreate(allocator, kEngineMessageProtocolVersion, error); | |
375 | if (!SOSMessageSetManifests(message, sender, base, proposed, includeManifestDeltas, NULL, error)) | |
376 | CFReleaseNull(message); | |
377 | return message; | |
378 | } | |
379 | ||
380 | bool SOSMessageSetManifests(SOSMessageRef message, SOSManifestRef sender, | |
381 | SOSManifestRef base, SOSManifestRef proposed, | |
382 | bool includeManifestDeltas, SOSManifestRef objectsSent, | |
383 | CFErrorRef *error) { | |
384 | if (!message) return true; | |
385 | bool ok = true; | |
386 | // TODO: Check at v2 encoding time | |
387 | // if (!sender) return (SOSMessageRef)SOSErrorCreate(kSOSErrorProcessingFailure, error, NULL, CFSTR("no sender manifest specified for SOSMessage")); | |
388 | message->baseDigest = CFRetainSafe(SOSManifestGetDigest(base, NULL)); | |
ecaf5866 | 389 | secinfo("engine", "SOSMessageSetManifests: setting base digest to %@ %zu", message->baseDigest, SOSManifestGetCount(base)); |
d8f41ccd | 390 | message->proposedDigest = CFRetainSafe(SOSManifestGetDigest(proposed, NULL)); |
ecaf5866 | 391 | secinfo("engine", "SOSMessageSetManifests: setting proposed digest to %@ %zu", message->proposedDigest, SOSManifestGetCount(proposed)); |
d8f41ccd | 392 | message->senderDigest = CFRetainSafe(SOSManifestGetDigest(sender, NULL)); |
ecaf5866 A |
393 | secinfo("engine", "SOSMessageSetManifests: setting sender digest to %@ %zu", message->senderDigest, SOSManifestGetCount(sender)); |
394 | ||
d8f41ccd A |
395 | if (includeManifestDeltas) { |
396 | SOSManifestRef additions = NULL; | |
397 | ok = SOSManifestDiff(base, proposed, &message->removals, &additions, error); | |
398 | if (message->version == 0) { | |
399 | message->additions = additions; | |
400 | } else { | |
401 | message->additions = SOSManifestCreateComplement(objectsSent, additions, error); | |
402 | CFReleaseSafe(additions); | |
403 | } | |
404 | } | |
405 | return ok; | |
406 | } | |
407 | ||
408 | void SOSMessageSetFlags(SOSMessageRef message, SOSMessageFlags flags) { | |
409 | message->flags = flags; | |
410 | } | |
411 | ||
412 | // Add an extension to this message | |
413 | void SOSMessageAddExtension(SOSMessageRef message, CFDataRef oid, bool isCritical, CFDataRef extension) { | |
414 | // TODO: Implement | |
415 | secerror("not implemented yet!"); | |
416 | } | |
417 | ||
418 | static bool SecMessageIsObjectValid(CFDataRef object, CFErrorRef *error) { | |
419 | const uint8_t *der = CFDataGetBytePtr(object); | |
420 | const uint8_t *der_end = der + CFDataGetLength(object); | |
421 | ccder_tag tag = 0; | |
422 | size_t len = 0; | |
423 | der = ccder_decode_tag(&tag, der, der_end); | |
424 | if (!der ) | |
425 | return SOSErrorCreate(kSOSErrorBadFormat, error, NULL, CFSTR("Invalid DER, no tag found")); | |
426 | if (tag == CCDER_EOL) | |
427 | return SOSErrorCreate(kSOSErrorBadFormat, error, NULL, CFSTR("Object has EOL tag")); | |
428 | der = ccder_decode_len(&len, der, der_end); | |
429 | if (!der) | |
430 | return SOSErrorCreate(kSOSErrorBadFormat, error, NULL, CFSTR("Object with tag %lu has no valid DER length"), tag); | |
431 | der += len; | |
432 | if (der_end - der) | |
433 | return SOSErrorCreate(kSOSErrorBadFormat, error, NULL, CFSTR("Object has %td trailing unused bytes"), der_end - der); | |
434 | return true; | |
435 | } | |
436 | ||
437 | bool SOSMessageAppendObject(SOSMessageRef message, CFDataRef object, CFErrorRef *error) { | |
438 | if (!SecMessageIsObjectValid(object, error)) return false; | |
439 | if (!message->objects) | |
440 | message->objects = CFArrayCreateMutableForCFTypes(CFGetAllocator(message)); | |
441 | if (message->objects) | |
442 | CFArrayAppendValue(message->objects, object); | |
443 | return true; | |
444 | } | |
445 | ||
d64be36e A |
446 | static |
447 | size_t ccder_sizeof_bit_string(cc_size n, const cc_unit *_Nonnull s) { | |
d8f41ccd A |
448 | return ccder_sizeof(CCDER_BIT_STRING, ccn_sizeof(ccn_bitlen(n, s)) + 1); |
449 | } | |
450 | ||
d64be36e A |
451 | static |
452 | uint8_t *ccder_encode_bit_string(cc_size n, const cc_unit *_Nonnull s, const uint8_t *_Nonnull der, uint8_t *_Nonnull der_end) { | |
d8f41ccd A |
453 | size_t bits = ccn_bitlen(n, s); |
454 | size_t out_size = ccn_sizeof(bits) + 1; | |
455 | der_end = ccder_encode_body_nocopy(out_size, der, der_end); | |
456 | if (der_end) | |
457 | ccn_write_uint_padded(n, s, out_size, der_end); | |
458 | return ccder_encode_tl(CCDER_BIT_STRING, out_size, der, der_end); | |
459 | } | |
460 | ||
461 | ||
5c19dc3a | 462 | static |
d8f41ccd A |
463 | size_t der_sizeof_implicit_data(ccder_tag tag, CFDataRef data) { |
464 | if (!data) | |
465 | return 0; | |
466 | return ccder_sizeof_implicit_raw_octet_string(tag, CFDataGetLength(data)); | |
467 | } | |
468 | ||
469 | ||
d64be36e A |
470 | static |
471 | uint8_t *der_encode_implicit_data(ccder_tag tag, CFDataRef data, const uint8_t *_Nonnull der, uint8_t *_Nonnull der_end) { | |
d8f41ccd A |
472 | if (!data) |
473 | return der_end; | |
474 | return ccder_encode_implicit_raw_octet_string(tag, CFDataGetLength(data), CFDataGetBytePtr(data), der, der_end); | |
475 | } | |
476 | ||
477 | static size_t der_sizeof_message_header(SOSMessageRef message, CFErrorRef *error) { | |
478 | if (!message->senderDigest) { | |
479 | // TODO: Create Error. | |
480 | return 0; | |
481 | } | |
482 | cc_unit flags[1]; | |
483 | flags[0] = (cc_unit)message->flags; // TODO Fix cast or something | |
484 | ||
485 | return ccder_sizeof(CCDER_CONSTRUCTED_SEQUENCE, | |
486 | der_sizeof_generalizedtime(message->creationTime, error) + | |
487 | ccder_sizeof_uint64(message->sequenceNumber) + | |
488 | ccder_sizeof_bit_string(array_size(flags), flags) + | |
489 | der_sizeof_implicit_data(CCDER_OCTET_STRING, message->senderDigest) + | |
490 | der_sizeof_implicit_data(0 | CCDER_CONTEXT_SPECIFIC, message->baseDigest) + | |
491 | der_sizeof_implicit_data(1 | CCDER_CONTEXT_SPECIFIC, message->proposedDigest)); | |
492 | } | |
493 | ||
494 | static uint8_t *der_encode_message_header(SOSMessageRef message, CFErrorRef *error, const uint8_t *der, uint8_t *der_end) { | |
495 | if (!message->senderDigest) { | |
496 | // TODO: Create Error. | |
497 | return NULL; | |
498 | } | |
499 | cc_unit flags[1]; | |
500 | flags[0] = (cc_unit)message->flags; // TODO Fix cast or something | |
501 | return ccder_encode_constructed_tl(CCDER_CONSTRUCTED_SEQUENCE, der_end, der, | |
502 | der_encode_generalizedtime(message->creationTime, error, der, | |
503 | ccder_encode_uint64(message->sequenceNumber, der, | |
504 | ccder_encode_bit_string(array_size(flags), flags, der, | |
505 | der_encode_implicit_data(CCDER_OCTET_STRING, message->senderDigest, der, | |
506 | der_encode_implicit_data(0 | CCDER_CONTEXT_SPECIFIC, message->baseDigest, der, | |
507 | der_encode_implicit_data(1 | CCDER_CONTEXT_SPECIFIC, message->proposedDigest, der, der_end))))))); | |
508 | } | |
509 | ||
510 | static size_t der_sizeof_deltas(SOSMessageRef message) { | |
511 | if (!message->additions && !message->removals) return 0; | |
512 | if (message->version == 0) { | |
513 | return ccder_sizeof(CCDER_OCTET_STRING, SOSManifestGetSize(message->removals))+ | |
514 | ccder_sizeof(CCDER_OCTET_STRING, SOSManifestGetSize(message->additions)); | |
515 | } else { | |
516 | return ccder_sizeof(0 | CCDER_CONTEXT_SPECIFIC | CCDER_CONSTRUCTED, | |
517 | ccder_sizeof(CCDER_OCTET_STRING, SOSManifestGetSize(message->removals))+ | |
518 | ccder_sizeof(CCDER_OCTET_STRING, SOSManifestGetSize(message->additions))); | |
519 | } | |
520 | } | |
521 | ||
522 | static uint8_t *der_encode_deltas(SOSMessageRef message, CFErrorRef *error, const uint8_t *der, uint8_t *der_end) { | |
523 | if (!message->additions && !message->removals) return der_end; | |
524 | if (message->version == 0) { | |
525 | return der_encode_implicit_data(CCDER_OCTET_STRING, SOSManifestGetData(message->removals), der, | |
526 | der_encode_implicit_data(CCDER_OCTET_STRING, SOSManifestGetData(message->additions), der, der_end)); | |
527 | } else { | |
528 | return ccder_encode_constructed_tl(0 | CCDER_CONTEXT_SPECIFIC | CCDER_CONSTRUCTED, der_end, der, | |
529 | der_encode_implicit_data(CCDER_OCTET_STRING, SOSManifestGetData(message->removals), der, | |
530 | der_encode_implicit_data(CCDER_OCTET_STRING, SOSManifestGetData(message->additions), der, der_end))); | |
531 | } | |
532 | } | |
533 | ||
534 | static size_t der_sizeof_extensions(SOSMessageRef message) { | |
535 | // We don't support any yet. | |
536 | return 0; | |
537 | } | |
538 | ||
539 | static uint8_t *der_encode_extensions(SOSMessageRef message, CFErrorRef *error, const uint8_t *der, uint8_t *der_end) { | |
540 | // We don't support any yet. | |
541 | return der_end; | |
542 | } | |
543 | ||
544 | static size_t der_sizeof_objects(SOSMessageRef message) { | |
545 | size_t len = 0; | |
546 | if (message->objects) { | |
547 | CFDataRef data; | |
548 | CFArrayForEachC(message->objects, data) { | |
549 | len += (size_t)CFDataGetLength(data); | |
550 | } | |
551 | } else if (message->version != 0) | |
552 | return 0; | |
553 | ||
554 | if (message->indefiniteLength) | |
555 | return len + 4; | |
556 | else | |
557 | return ccder_sizeof(2 | CCDER_CONTEXT_SPECIFIC | CCDER_CONSTRUCTED, len); | |
558 | } | |
559 | ||
560 | static uint8_t *der_encode_objects(SOSMessageRef message, CFErrorRef *error, const uint8_t *der, uint8_t *der_end) { | |
561 | if (!message->objects && message->version != 0) return der_end; | |
562 | const uint8_t *original_der_end = der_end; | |
563 | if (message->indefiniteLength) | |
564 | der_end = ccder_encode_tl(CCDER_EOL, 0, der, der_end); | |
565 | ||
566 | for (CFIndex position = (message->objects ? CFArrayGetCount(message->objects) : 0) - 1; position >= 0; --position) { | |
567 | CFDataRef object = CFArrayGetValueAtIndex(message->objects, position); | |
568 | der_end = ccder_encode_body(CFDataGetLength(object), CFDataGetBytePtr(object), der, der_end); | |
569 | } | |
570 | if (message->indefiniteLength) { | |
571 | return ccder_encode_tag(2 | CCDER_CONTEXT_SPECIFIC | CCDER_CONSTRUCTED, der, | |
572 | ccder_encode_len(0, der, der_end)); | |
573 | } else { | |
574 | ccder_tag otag = message->version == 0 ? CCDER_CONSTRUCTED_SEQUENCE : 2 | CCDER_CONTEXT_SPECIFIC | CCDER_CONSTRUCTED; | |
575 | return ccder_encode_constructed_tl(otag, original_der_end, der, der_end); | |
576 | } | |
577 | } | |
578 | ||
579 | static size_t der_sizeof_v2_message(SOSMessageRef message, CFErrorRef *error) { | |
580 | size_t body_size = (der_sizeof_message_header(message, error) + | |
581 | der_sizeof_deltas(message) + | |
582 | der_sizeof_extensions(message) + | |
583 | der_sizeof_objects(message)); | |
584 | if (message->indefiniteLength) | |
585 | return body_size + 4; | |
586 | else | |
587 | return ccder_sizeof(CCDER_CONSTRUCTED_SEQUENCE, body_size); | |
588 | } | |
589 | ||
590 | ||
591 | static uint8_t *der_encode_v2_message(SOSMessageRef message, CFErrorRef *error, const uint8_t *der, uint8_t *der_end) { | |
592 | const uint8_t *original_der_end = der_end; | |
593 | if (message->indefiniteLength) | |
594 | der_end = ccder_encode_tl(CCDER_EOL, 0, der, der_end); | |
595 | ||
596 | der_end = der_encode_message_header(message, error, der, | |
597 | der_encode_deltas(message, error, der, | |
598 | der_encode_extensions(message, error, der, | |
599 | der_encode_objects(message, error, der, der_end)))); | |
600 | ||
601 | if (message->indefiniteLength) { | |
602 | return ccder_encode_tag(CCDER_CONSTRUCTED_SEQUENCE, der, | |
603 | ccder_encode_len(0, der, der_end)); | |
604 | } else { | |
605 | return ccder_encode_constructed_tl(CCDER_CONSTRUCTED_SEQUENCE, original_der_end, der, der_end); | |
606 | } | |
607 | } | |
608 | ||
609 | //------------------------------------------------------------------------------------------------------------------------------------ | |
610 | // V1 support | |
611 | //------------------------------------------------------------------------------------------------------------------------------------ | |
612 | ||
613 | /* ManifestDigest message */ | |
614 | static size_t der_sizeof_manifest_digest_message(SOSMessageRef message, CFErrorRef *error) { | |
615 | if (!message->senderDigest || CFDataGetLength(message->senderDigest) != SOSDigestSize) { | |
616 | SOSErrorCreate(kSOSErrorProcessingFailure, error, NULL, CFSTR("digest length mismatch")); | |
617 | return 0; | |
618 | } | |
619 | return ccder_sizeof(CCDER_CONSTRUCTED_SEQUENCE, | |
620 | (ccder_sizeof_uint64(SOSManifestDigestMessageType) + | |
621 | ccder_sizeof_raw_octet_string(SOSDigestSize))); | |
622 | } | |
623 | ||
624 | static uint8_t *der_encode_manifest_digest_message(SOSMessageRef message, CFErrorRef *error, const uint8_t *der, uint8_t *der_end) { | |
ecaf5866 | 625 | secinfo("engine", "der_encode_manifest_digest_message: encoded sender digest as %@", message->senderDigest); |
d8f41ccd A |
626 | return ccder_encode_constructed_tl(CCDER_CONSTRUCTED_SEQUENCE, der_end, der, |
627 | ccder_encode_uint64(SOSManifestDigestMessageType, der, | |
628 | ccder_encode_raw_octet_string(SOSDigestSize, CFDataGetBytePtr(message->senderDigest), der, der_end))); | |
629 | } | |
630 | ||
631 | /* Manifest message */ | |
632 | static size_t der_sizeof_manifest_message(SOSMessageRef message, CFErrorRef *error) { | |
633 | if (!message->additions) { | |
634 | SOSErrorCreate(kSOSErrorProcessingFailure, error, NULL, CFSTR("no manifest for manifest message")); | |
635 | return 0; | |
636 | } | |
637 | return ccder_sizeof(CCDER_CONSTRUCTED_SEQUENCE, | |
638 | (ccder_sizeof_uint64(SOSManifestMessageType) + | |
639 | der_sizeof_implicit_data(CCDER_OCTET_STRING, SOSManifestGetData(message->additions)))); | |
640 | } | |
641 | ||
642 | static uint8_t *der_encode_manifest_message(SOSMessageRef message, CFErrorRef *error, const uint8_t *der, uint8_t *der_end) { | |
ecaf5866 | 643 | secinfo("engine", "der_encode_manifest_message: encoded message additions as (%zu, %@)", SOSManifestGetCount(message->additions), SOSManifestGetDigest(message->additions, NULL)); |
d8f41ccd A |
644 | return ccder_encode_constructed_tl(CCDER_CONSTRUCTED_SEQUENCE, der_end, der, |
645 | ccder_encode_uint64(SOSManifestMessageType, der, | |
646 | der_encode_implicit_data(CCDER_OCTET_STRING, SOSManifestGetData(message->additions), der, der_end))); | |
647 | } | |
648 | ||
649 | /* ManifestDeltaAndObjects message */ | |
650 | static size_t der_sizeof_manifest_and_objects_message(SOSMessageRef message, CFErrorRef *error) { | |
651 | if (!message->baseDigest || CFDataGetLength(message->baseDigest) != SOSDigestSize) { | |
652 | SOSErrorCreate(kSOSErrorProcessingFailure, error, NULL, CFSTR("digest length mismatch")); | |
653 | return 0; | |
654 | } | |
655 | ||
656 | return ccder_sizeof(CCDER_CONSTRUCTED_SEQUENCE, | |
657 | (ccder_sizeof_uint64(SOSManifestDeltaAndObjectsMessageType) + | |
658 | ccder_sizeof(CCDER_CONSTRUCTED_SEQUENCE, | |
659 | (ccder_sizeof_raw_octet_string(SOSDigestSize) + | |
660 | der_sizeof_implicit_data(CCDER_OCTET_STRING, SOSManifestGetData(message->removals)) + | |
661 | der_sizeof_implicit_data(CCDER_OCTET_STRING, SOSManifestGetData(message->additions)) + | |
662 | der_sizeof_objects(message))))); | |
663 | } | |
664 | ||
665 | static uint8_t *der_encode_manifest_and_objects_message(SOSMessageRef message, CFErrorRef *error, const uint8_t *der, uint8_t *der_end) { | |
ecaf5866 | 666 | secinfo("engine", "der_encode_manifest_and_objects_message: encoded base digest as %@", message->baseDigest); |
d8f41ccd A |
667 | return ccder_encode_constructed_tl(CCDER_CONSTRUCTED_SEQUENCE, der_end, der, |
668 | ccder_encode_uint64(SOSManifestDeltaAndObjectsMessageType, der, | |
669 | ccder_encode_constructed_tl(CCDER_CONSTRUCTED_SEQUENCE, der_end, der, | |
670 | ccder_encode_raw_octet_string(SOSDigestSize, CFDataGetBytePtr(message->baseDigest), der, | |
671 | der_encode_implicit_data(CCDER_OCTET_STRING, SOSManifestGetData(message->removals), der, | |
672 | der_encode_implicit_data(CCDER_OCTET_STRING, SOSManifestGetData(message->additions), der, | |
673 | der_encode_objects(message, error, der, der_end))))))); | |
674 | } | |
675 | ||
676 | static uint64_t SOSMessageInferType(SOSMessageRef message, CFErrorRef *error) { | |
677 | if (message->baseDigest) { | |
678 | // TODO: Assert that we don't have senderDigest or proposedDigest | |
679 | if (SOSManifestGetCount(message->removals) || SOSManifestGetCount(message->additions) || SOSMessageCountObjects(message)) { | |
680 | return SOSManifestDeltaAndObjectsMessageType; | |
681 | } else { | |
682 | // NOTE: If we force a SOSManifestDeltaAndObjectsMessageType instead then | |
683 | // true v0 peers will overwrite their last objects message to us. However this | |
684 | // implements the current v0 behaviour | |
685 | return SOSManifestDigestMessageType; | |
686 | } | |
687 | } else if (message->additions) { | |
688 | // TODO: Assert that we don't have senderDigest, proposedDigest, additions, removals or objects | |
689 | return SOSManifestMessageType; | |
690 | } else if (message->senderDigest) { | |
691 | // TODO: Assert that we don't have proposedDigest, removals or objects | |
692 | return SOSManifestDigestMessageType; | |
693 | } | |
694 | // TODO: Create error. | |
695 | return SOSManifestInvalidMessageType; | |
696 | } | |
697 | ||
698 | static size_t der_sizeof_message(SOSMessageRef message, uint64_t messageType, CFErrorRef *error) { | |
699 | switch (messageType) { | |
700 | case SOSManifestInvalidMessageType: | |
701 | return der_sizeof_v2_message(message, error); | |
702 | case SOSManifestDigestMessageType: | |
703 | return der_sizeof_manifest_digest_message(message, error); | |
704 | case SOSManifestMessageType: | |
705 | return der_sizeof_manifest_message(message, error); | |
706 | case SOSManifestDeltaAndObjectsMessageType: | |
707 | return der_sizeof_manifest_and_objects_message(message, error); | |
708 | } | |
709 | return 0; | |
710 | } | |
711 | ||
712 | static uint8_t *der_encode_message(SOSMessageRef message, uint64_t messageType, CFErrorRef *error, const uint8_t *der, uint8_t *der_end) { | |
713 | switch (messageType) { | |
714 | case SOSManifestInvalidMessageType: | |
715 | return der_encode_v2_message(message, error, der, der_end); | |
716 | case SOSManifestDigestMessageType: | |
717 | return der_encode_manifest_digest_message(message, error, der, der_end); | |
718 | case SOSManifestMessageType: | |
719 | return der_encode_manifest_message(message, error, der, der_end); | |
720 | case SOSManifestDeltaAndObjectsMessageType: | |
721 | return der_encode_manifest_and_objects_message(message, error, der, der_end); | |
722 | } | |
723 | return der_end; | |
724 | } | |
725 | ||
726 | // Encode an SOSMessage, calls addObject callback and appends returned objects | |
727 | // one by one, until addObject returns NULL. | |
728 | CFDataRef SOSMessageCreateData(SOSMessageRef message, uint64_t sequenceNumber, CFErrorRef *error) { | |
729 | // Version 2 message have sequence numbers, version 0 messages do not. | |
730 | uint64_t messageType = SOSManifestInvalidMessageType; | |
731 | message->sequenceNumber = sequenceNumber; | |
732 | if (message->version == 0) { | |
733 | message->indefiniteLength = false; | |
734 | messageType = SOSMessageInferType(message, error); | |
735 | if (!messageType) { | |
736 | // Propagate error | |
737 | return NULL; | |
738 | } | |
739 | } else { | |
740 | message->creationTime = floor(CFAbsoluteTimeGetCurrent()); | |
741 | } | |
742 | size_t der_size = der_sizeof_message(message, messageType, error); | |
743 | CFMutableDataRef data = CFDataCreateMutable(NULL, der_size); | |
744 | if (data == NULL) { | |
745 | // TODO Error. | |
746 | return NULL; | |
747 | } | |
748 | CFDataSetLength(data, der_size); | |
749 | uint8_t *der_end = CFDataGetMutableBytePtr(data); | |
750 | const uint8_t *der = der_end; | |
751 | der_end += der_size; | |
752 | ||
753 | der_end = der_encode_message(message, messageType, error, der, der_end); | |
754 | if (der != der_end) { | |
755 | secwarning("internal error %td bytes unused in der buffer", der_end - der); | |
756 | } | |
757 | return data; | |
758 | } | |
759 | ||
760 | // | |
761 | // MARK: SOSMessage decoding | |
762 | // | |
763 | ||
764 | #define CCBER_LEN_INDEFINITE ((size_t)-1) | |
765 | ||
766 | // Decode BER length field. Sets *lenp to ccber_indefinite_len if this is an indefinite length encoded object. | |
767 | // Behaves like ccder_decode_len in every other way. | |
d64be36e A |
768 | static |
769 | const uint8_t *ccber_decode_len(size_t *_Nonnull lenp, const uint8_t *_Nullable der, const uint8_t *_Nonnull der_end) { | |
d8f41ccd A |
770 | if (der && der < der_end) { |
771 | if (*der == 0x80) { | |
772 | der++; | |
773 | *lenp = CCBER_LEN_INDEFINITE; | |
774 | } | |
775 | else | |
776 | der = ccder_decode_len(lenp, der, der_end); | |
777 | } | |
778 | return der; | |
779 | } | |
780 | ||
781 | static const uint8_t *der_decode_generalizedtime(CFAbsoluteTime *at, CFErrorRef *error, const uint8_t *der, const uint8_t *der_end) { | |
782 | const uint8_t *times_end = NULL; | |
783 | der = ccder_decode_constructed_tl(CCDER_GENERALIZED_TIME, ×_end, der, der_end); | |
784 | der = der_decode_generalizedtime_body(at, error, der, times_end); | |
785 | if (times_end != der) { | |
786 | secwarning("internal error %td bytes unused in generalizedtime DER buffer", times_end - der); | |
787 | } | |
788 | return der; | |
789 | } | |
790 | ||
791 | static const uint8_t *der_decode_optional_generalizedtime(CFAbsoluteTime *at, CFErrorRef *error, const uint8_t *der, const uint8_t *der_end) { | |
792 | const uint8_t *times_end = der_decode_generalizedtime(at, error, der, der_end); | |
793 | return times_end ? times_end : der; | |
794 | } | |
795 | ||
d64be36e A |
796 | static |
797 | const uint8_t *ccder_decode_implicit_uint64(ccder_tag expected_tag, uint64_t *_Nonnull r, const uint8_t *_Nullable der, const uint8_t *_Nonnull der_end) { | |
d8f41ccd A |
798 | size_t len; |
799 | der = ccder_decode_tl(expected_tag, &len, der, der_end); | |
800 | if (der && len && (*der & 0x80) != 0x80) { | |
5c19dc3a | 801 | if (ccn_read_uint(ccn_nof_size(sizeof(*r)), (cc_unit*)r, len, der) >= 0) |
d8f41ccd A |
802 | return der + len; |
803 | } | |
804 | return NULL; | |
805 | } | |
806 | ||
807 | 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) { | |
808 | const uint8_t *ui64_end = ccder_decode_implicit_uint64(expected_tag, value, der, der_end); | |
809 | return ui64_end ? ui64_end : der; | |
810 | } | |
811 | ||
812 | ||
813 | static const uint8_t *ccder_decode_optional_uint64(uint64_t *value, const uint8_t *der, const uint8_t *der_end) { | |
814 | const uint8_t *ui64_end = ccder_decode_uint64(value, der, der_end); | |
815 | return ui64_end ? ui64_end : der; | |
816 | } | |
817 | ||
818 | static const uint8_t *ccder_decode_digest_types(SOSMessageRef message, const uint8_t *der, const uint8_t *der_end) { | |
819 | const uint8_t *dt_end; | |
820 | der = ccder_decode_sequence_tl(&dt_end, der, der_end); | |
821 | if (!der) return NULL; | |
822 | // Skip over digestType body for now. | |
823 | // TODO: Support DigestType | |
824 | return dt_end; | |
825 | } | |
826 | ||
827 | static const uint8_t *ccder_decode_optional_digest_types(SOSMessageRef message, const uint8_t *der, const uint8_t *der_end) { | |
828 | const uint8_t *dt_end = ccder_decode_digest_types(message, der, der_end); | |
829 | return dt_end ? dt_end : der; | |
830 | } | |
831 | ||
832 | 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) { | |
833 | size_t len; | |
834 | const uint8_t *body = ccder_decode_tl(CCDER_BIT_STRING, &len, der, der_end); | |
835 | if (!body || len < 1) | |
836 | return NULL; | |
837 | ||
838 | if (r_bitlen) *r_bitlen = (len - 1) * 8 - (body[0] & 7); | |
839 | ccn_read_uint(1, r, len - 1, body + 1); | |
840 | return body + len; | |
841 | } | |
842 | ||
843 | static const uint8_t *der_decode_implicit_data(ccder_tag expected_tag, CFDataRef *data, const uint8_t *der, const uint8_t *der_end) { | |
844 | size_t len = 0; | |
845 | der = ccder_decode_tl(expected_tag, &len, der, der_end); | |
846 | if (der && data) { | |
847 | *data = CFDataCreateWithBytesNoCopy(kCFAllocatorDefault, der, len, kCFAllocatorNull); | |
848 | if (*data) | |
849 | der += len; | |
850 | else | |
851 | der = NULL; | |
852 | } | |
853 | return der; | |
854 | } | |
855 | ||
856 | static const uint8_t *der_decode_optional_implicit_data(ccder_tag expected_tag, CFDataRef *data, const uint8_t *der, const uint8_t *der_end) { | |
857 | const uint8_t *data_end = der_decode_implicit_data(expected_tag, data, der, der_end); | |
858 | return data_end ? data_end : der; | |
859 | } | |
860 | ||
861 | static const uint8_t *der_decode_deltas_body(SOSMessageRef message, CFErrorRef *error, const uint8_t *der, const uint8_t *der_end) { | |
862 | CFDataRef removals = NULL, additions = NULL; | |
863 | der = der_decode_implicit_data(CCDER_OCTET_STRING, &removals, der, der_end); | |
864 | der = der_decode_implicit_data(CCDER_OCTET_STRING, &additions, der, der_end); | |
865 | if (der) { | |
866 | message->removals = SOSManifestCreateWithData(removals, error); | |
867 | message->additions = SOSManifestCreateWithData(additions, error); | |
868 | if (!message->removals || !message->additions) { | |
869 | CFReleaseNull(message->removals); | |
870 | CFReleaseNull(message->additions); | |
871 | der = NULL; | |
872 | } | |
873 | } | |
874 | CFReleaseSafe(removals); | |
875 | CFReleaseSafe(additions); | |
876 | ||
877 | return der; | |
878 | } | |
879 | ||
880 | static const uint8_t *der_decode_deltas(SOSMessageRef message, CFErrorRef *error, const uint8_t *der, const uint8_t *der_end) { | |
881 | const uint8_t *deltas_end = NULL; | |
882 | der = ccder_decode_constructed_tl(0 | CCDER_CONTEXT_SPECIFIC | CCDER_CONSTRUCTED, &deltas_end, der, der_end); | |
883 | return der_decode_deltas_body(message, error, der, deltas_end); | |
884 | } | |
885 | ||
886 | static const uint8_t *der_decode_optional_deltas(SOSMessageRef message, const uint8_t *der, const uint8_t *der_end) { | |
887 | const uint8_t *seq_end = der_decode_deltas(message, NULL, der, der_end); | |
888 | return seq_end ? seq_end : der; | |
889 | } | |
890 | ||
891 | static const uint8_t *der_decode_extensions(SOSMessageRef message, CFErrorRef *error, const uint8_t *der, const uint8_t *der_end) { | |
892 | const uint8_t *extensions_end; | |
893 | der = ccder_decode_constructed_tl(1 | CCDER_CONTEXT_SPECIFIC | CCDER_CONSTRUCTED, &extensions_end, der, der_end); | |
894 | if (!der) return NULL; | |
895 | // Skip over extensions for now. | |
896 | return extensions_end; | |
897 | } | |
898 | ||
899 | static const uint8_t *der_decode_optional_extensions(SOSMessageRef message, CFErrorRef *error, const uint8_t *der, const uint8_t *der_end) { | |
900 | const uint8_t *extensions_end = der_decode_extensions(message, NULL, der, der_end); | |
901 | return extensions_end ? extensions_end : der; | |
902 | } | |
903 | ||
904 | 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)) { | |
905 | bool stop = false; | |
906 | ccder_tag tag; | |
907 | // Look ahead at the tag | |
908 | while (!stop && ccder_decode_tag(&tag, der, der_end) && tag != CCDER_EOL) { | |
909 | const uint8_t *object_end = NULL; | |
910 | if (!ccder_decode_constructed_tl(tag, &object_end, der, der_end)) { | |
911 | SOSErrorCreate(kSOSErrorDecodeFailure, error, NULL, CFSTR("failed to decode object header")); | |
912 | return NULL; | |
913 | } | |
914 | if (withObject) { | |
915 | CFDataRef object = CFDataCreateWithBytesNoCopy(kCFAllocatorDefault, der, object_end - der, kCFAllocatorNull); | |
916 | withObject(object, &stop); | |
917 | CFReleaseSafe(object); | |
918 | } | |
919 | der = object_end; | |
920 | } | |
921 | if (length == CCBER_LEN_INDEFINITE) { | |
922 | size_t len = 0; | |
923 | der = ccder_decode_tl(CCDER_EOL, &len, der, der_end); | |
924 | if (len != 0) { | |
925 | secwarning("%td length ", der_end - der); | |
926 | } | |
927 | } | |
928 | if (!stop && der != der_end) | |
929 | secwarning("%td trailing bytes after objects DER", der_end - der); | |
930 | ||
931 | return der; | |
932 | } | |
933 | ||
934 | static const uint8_t *der_decode_objects(SOSMessageRef message, CFErrorRef *error, const uint8_t *der, const uint8_t *der_end) { | |
935 | ccder_tag tag = 0; | |
936 | size_t objects_len = 0; | |
937 | der = ccder_decode_tag(&tag, der, der_end); | |
938 | if (tag != (2 | CCDER_CONTEXT_SPECIFIC | CCDER_CONSTRUCTED)) return NULL; | |
939 | der = ccber_decode_len(&objects_len, der, der_end); | |
940 | if (objects_len != CCBER_LEN_INDEFINITE && der_end - der != (ptrdiff_t)objects_len) { | |
941 | secwarning("%td trailing bytes after SOSMessage DER", (der_end - der) - (ptrdiff_t)objects_len); | |
942 | } | |
943 | // Remember a pointer into message->der where objects starts. | |
944 | message->objectsDer = der; | |
945 | message->objectsLen = objects_len; | |
946 | ||
947 | return der + objects_len; | |
948 | } | |
949 | ||
950 | static const uint8_t *der_decode_optional_objects(SOSMessageRef message, CFErrorRef *error, const uint8_t *der, const uint8_t *der_end) { | |
951 | const uint8_t *seq_end = der_decode_objects(message, NULL, der, der_end); | |
952 | return seq_end ? seq_end : der; | |
953 | } | |
954 | ||
d8f41ccd A |
955 | static const uint8_t *der_decode_message_header(SOSMessageRef message, CFErrorRef *error, const uint8_t *der, const uint8_t *der_end) { |
956 | cc_unit flags[1] = {}; | |
957 | der = ccder_decode_constructed_tl(CCDER_CONSTRUCTED_SEQUENCE, &der_end, der, der_end); | |
958 | message->version = 2; | |
959 | der = ccder_decode_optional_implicit_uint64(0 | CCDER_CONTEXT_SPECIFIC, &message->version, der, der_end); | |
960 | der = der_decode_optional_generalizedtime(&message->creationTime, error, der, der_end); | |
961 | der = ccder_decode_optional_uint64(&message->sequenceNumber, der, der_end); | |
962 | der = ccder_decode_optional_digest_types(message, der, der_end); | |
963 | der = ccder_decode_bit_string(array_size(flags), NULL, flags, der, der_end); | |
964 | message->flags = flags[0]; | |
965 | ||
966 | der = der_decode_implicit_data(CCDER_OCTET_STRING, &message->senderDigest, der, der_end); | |
ecaf5866 A |
967 | secinfo("engine", "der_decode_message_header: decoded sender digest as %@", message->senderDigest); |
968 | ||
d8f41ccd | 969 | der = der_decode_optional_implicit_data(0 | CCDER_CONTEXT_SPECIFIC, &message->baseDigest, der, der_end); |
ecaf5866 A |
970 | secinfo("engine", "der_decode_message_header: decoded base digest as %@", message->baseDigest); |
971 | ||
d8f41ccd | 972 | der = der_decode_optional_implicit_data(1 | CCDER_CONTEXT_SPECIFIC, &message->proposedDigest, der, der_end); |
ecaf5866 A |
973 | secinfo("engine", "der_decode_message_header: decoded proposed digest as %@", message->proposedDigest); |
974 | ||
d8f41ccd A |
975 | return der; |
976 | } | |
977 | ||
978 | static const uint8_t * | |
979 | der_decode_manifest_and_objects_message(SOSMessageRef message, | |
980 | CFErrorRef *error, const uint8_t *der, | |
981 | const uint8_t *der_end) { | |
982 | size_t objects_len = 0; | |
983 | const uint8_t *body_end; | |
984 | der = ccder_decode_sequence_tl(&body_end, der, der_end); | |
985 | if (body_end != der_end) { | |
5c19dc3a | 986 | SOSErrorCreate(kSOSErrorInvalidMessage, error, NULL, CFSTR("Trailing garbage at end of message")); |
d8f41ccd A |
987 | return NULL; |
988 | } | |
989 | der = der_decode_implicit_data(CCDER_OCTET_STRING, &message->baseDigest, der, body_end); | |
ecaf5866 A |
990 | secinfo("engine", "der_decode_manifest_and_objects_message: decoded base digest as %@", message->baseDigest); |
991 | ||
d8f41ccd A |
992 | der = der_decode_deltas_body(message, error, der, body_end); |
993 | // Remember a pointer into message->der where objects starts. | |
994 | der = message->objectsDer = ccder_decode_tl(CCDER_CONSTRUCTED_SEQUENCE, &objects_len, der, body_end); | |
995 | message->objectsLen = objects_len; | |
996 | ||
997 | return der ? der + objects_len : NULL; | |
998 | } | |
999 | ||
1000 | static const uint8_t *der_decode_v0_message_body(SOSMessageRef message, CFErrorRef *error, const uint8_t *der, const uint8_t *der_end) { | |
1001 | uint64_t messageType = 0; | |
1002 | der = ccder_decode_uint64(&messageType, der, der_end); | |
1003 | if (der) switch (messageType) { | |
1004 | case SOSManifestDigestMessageType: | |
1005 | { | |
1006 | der = der_decode_implicit_data(CCDER_OCTET_STRING, &message->senderDigest, der, der_end); | |
ecaf5866 | 1007 | secinfo("engine", "der_decode_v0_message_body: received a DigestMessage with sender digest: %@", message->senderDigest); |
d8f41ccd A |
1008 | break; |
1009 | } | |
1010 | case SOSManifestMessageType: | |
1011 | { | |
1012 | CFDataRef manifestBody = NULL; | |
1013 | der = der_decode_implicit_data(CCDER_OCTET_STRING, &manifestBody, der, der_end); | |
1014 | if (!der) return NULL; | |
1015 | if (der != der_end) { | |
1016 | secwarning("%td trailing bytes after deltas DER", der_end - der); | |
1017 | } | |
1018 | message->additions = SOSManifestCreateWithData(manifestBody, error); | |
ecaf5866 | 1019 | secinfo("engine", "der_decode_v0_message_body: received a ManifestMessage with (%zu, %@)", SOSManifestGetCount(message->additions), SOSManifestGetDigest(message->additions, NULL)); |
d8f41ccd A |
1020 | CFReleaseSafe(manifestBody); |
1021 | break; | |
1022 | } | |
1023 | case SOSManifestDeltaAndObjectsMessageType: | |
1024 | { | |
1025 | der = der_decode_manifest_and_objects_message(message, error, der, der_end); | |
1026 | break; | |
1027 | } | |
1028 | default: | |
5c19dc3a | 1029 | SOSErrorCreate(kSOSErrorInvalidMessage, error, NULL, CFSTR("Invalid message type %llu"), messageType); |
d8f41ccd A |
1030 | break; |
1031 | } | |
1032 | return der; | |
1033 | } | |
1034 | ||
1035 | static const uint8_t *der_decode_message(SOSMessageRef message, CFErrorRef *error, const uint8_t *der, const uint8_t *der_end) { | |
1036 | ccder_tag tag = 0; | |
1037 | size_t body_len = 0; | |
1038 | ||
1039 | der = ccder_decode_tag(&tag, der, der_end); | |
1040 | if (tag != CCDER_CONSTRUCTED_SEQUENCE) return NULL; | |
1041 | der = ccber_decode_len(&body_len, der, der_end); | |
1042 | if (der && body_len && body_len != CCBER_LEN_INDEFINITE && (der_end - der) != (ptrdiff_t)body_len) { | |
1043 | secwarning("%td trailing bytes after SOSMessage DER", (der_end - der) - (ptrdiff_t)body_len); | |
1044 | der_end = der + body_len; | |
1045 | } | |
1046 | ||
1047 | if (ccder_decode_tag(&tag, der, der_end)) switch (tag) { | |
1048 | case CCDER_INTEGER: // v0 | |
1049 | if (body_len == CCBER_LEN_INDEFINITE) | |
1050 | der = NULL; // Not supported for v0 messages | |
1051 | else | |
1052 | der = der_decode_v0_message_body(message, error, der, der_end); | |
1053 | break; | |
1054 | case CCDER_CONSTRUCTED_SEQUENCE: //v2 | |
1055 | der = der_decode_message_header(message, error, der, der_end); | |
1056 | der = der_decode_optional_deltas(message, der, der_end); | |
1057 | der = der_decode_optional_extensions(message, error, der, der_end); | |
1058 | der = der_decode_optional_objects(message, error, der, der_end); | |
1059 | break; | |
1060 | } | |
1061 | return der; | |
1062 | } | |
1063 | ||
1064 | // Decode a SOSMessage | |
1065 | SOSMessageRef SOSMessageCreateWithData(CFAllocatorRef allocator, CFDataRef derData, CFErrorRef *error) { | |
1066 | if (!derData) | |
1067 | return (SOSMessageRef)SOSErrorCreate(kSOSErrorDecodeFailure, error, NULL, CFSTR("NULL data => no SOSMessage")); | |
1068 | SOSMessageRef message = CFTypeAllocate(SOSMessage, struct __OpaqueSOSMessage, allocator); | |
1069 | if (!message) | |
1070 | return (SOSMessageRef)SOSErrorCreate(kSOSErrorAllocationFailure, error, NULL, CFSTR("failed to alloc SOSMessage")); | |
1071 | message->der = CFRetainSafe(derData); | |
1072 | const uint8_t *der = CFDataGetBytePtr(derData); | |
1073 | const uint8_t *der_end = der + CFDataGetLength(derData); | |
1074 | der = der_decode_message(message, error, der, der_end); | |
5c19dc3a | 1075 | if (!der_end || der != der_end) { |
d8f41ccd A |
1076 | if (error && !*error) |
1077 | SOSErrorCreate(kSOSErrorDecodeFailure, error, NULL, CFSTR("SOSMessage DER decoding failure %td bytes left"), der_end - der); | |
1078 | return CFReleaseSafe(message); | |
1079 | } | |
1080 | return message; | |
1081 | } | |
1082 | ||
1083 | // Read values from a decoded messgage | |
1084 | ||
1085 | CFDataRef SOSMessageGetBaseDigest(SOSMessageRef message) { | |
1086 | return message->baseDigest; | |
1087 | } | |
1088 | ||
1089 | CFDataRef SOSMessageGetProposedDigest(SOSMessageRef message) { | |
1090 | return message->proposedDigest; | |
1091 | } | |
1092 | ||
1093 | CFDataRef SOSMessageGetSenderDigest(SOSMessageRef message) { | |
1094 | return message->senderDigest; | |
1095 | } | |
1096 | ||
1097 | SOSMessageFlags SOSMessageGetFlags(SOSMessageRef message) { | |
1098 | return message->flags; | |
1099 | } | |
1100 | ||
1101 | uint64_t SOSMessageGetSequenceNumber(SOSMessageRef message) { | |
1102 | return message->sequenceNumber; | |
1103 | } | |
1104 | ||
1105 | SOSManifestRef SOSMessageGetRemovals(SOSMessageRef message) { | |
1106 | return message->removals; | |
1107 | } | |
1108 | ||
1109 | SOSManifestRef SOSMessageGetAdditions(SOSMessageRef message) { | |
1110 | return message->additions; | |
1111 | } | |
1112 | ||
1113 | // Iterate though the extensions in a decoded SOSMessage. If criticalOnly is | |
1114 | // true all non critical extensions are skipped. | |
1115 | void SOSMessageWithExtensions(SOSMessageRef message, bool criticalOnly, void(^withExtension)(CFDataRef oid, bool isCritical, CFDataRef extension, bool *stop)) { | |
1116 | // TODO | |
1117 | } | |
1118 | ||
1119 | size_t SOSMessageCountObjects(SOSMessageRef message) { | |
1120 | if (message->objects) | |
1121 | return CFArrayGetCount(message->objects); | |
1122 | if (!message->objectsDer) | |
1123 | return 0; | |
1124 | const uint8_t *der = CFDataGetBytePtr(message->der); | |
1125 | const uint8_t *der_end = der + CFDataGetLength(message->der); | |
1126 | __block size_t count = 0; | |
1127 | der_foreach_objects(message->objectsLen, message->objectsDer, der_end, NULL, ^(CFDataRef object, bool *stop){ ++count; }); | |
1128 | return count; | |
1129 | } | |
1130 | ||
1131 | // Iterate though the objects in a decoded SOSMessage. | |
1132 | bool SOSMessageWithObjects(SOSMessageRef message, CFErrorRef *error, | |
1133 | void(^withObject)(CFDataRef object, bool *stop)) { | |
1134 | if (message->objects) { | |
1135 | CFDataRef object; | |
1136 | CFArrayForEachC(message->objects, object) { | |
1137 | bool stop = false; | |
1138 | withObject(object, &stop); | |
1139 | if (stop) | |
1140 | break; | |
1141 | } | |
1142 | return true; | |
1143 | } | |
1144 | if (!message->objectsDer) | |
1145 | return true; | |
1146 | const uint8_t *der = CFDataGetBytePtr(message->der); | |
1147 | const uint8_t *der_end = der + CFDataGetLength(message->der); | |
1148 | return der_foreach_objects(message->objectsLen, message->objectsDer, der_end, error, withObject); | |
1149 | } | |
1150 | ||
1151 | bool SOSMessageWithSOSObjects(SOSMessageRef message, SOSDataSourceRef dataSource, CFErrorRef *error, | |
1152 | void(^withObject)(SOSObjectRef object, bool *stop)) { | |
1153 | return SOSMessageWithObjects(message, error, ^(CFDataRef object, bool *stop) { | |
1154 | CFDictionaryRef plist = NULL; | |
1155 | const uint8_t *der = CFDataGetBytePtr(object); | |
1156 | const uint8_t *der_end = der + CFDataGetLength(object); | |
5c19dc3a | 1157 | // TODO Remove intermediate plist format |
d64be36e | 1158 | der = der_decode_dictionary(kCFAllocatorDefault, &plist, error, der, der_end); |
d8f41ccd A |
1159 | if (der) { |
1160 | SOSObjectRef peersObject = SOSObjectCreateWithPropertyList(dataSource, plist, error); | |
1161 | withObject(peersObject, stop); | |
1162 | CFReleaseSafe(peersObject); | |
1163 | } | |
1164 | CFReleaseSafe(plist); | |
1165 | }); | |
1166 | } |