1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27 package gov.nist.secauto.swid.builder.output;
28
29 import com.fasterxml.jackson.core.JsonFactory;
30 import com.fasterxml.jackson.core.JsonGenerator;
31
32 import gov.nist.secauto.swid.builder.AbstractLanguageSpecificBuilder;
33 import gov.nist.secauto.swid.builder.EntityBuilder;
34 import gov.nist.secauto.swid.builder.LinkBuilder;
35 import gov.nist.secauto.swid.builder.MetaBuilder;
36 import gov.nist.secauto.swid.builder.Role;
37 import gov.nist.secauto.swid.builder.SWIDBuilder;
38 import gov.nist.secauto.swid.builder.ValidationException;
39 import gov.nist.secauto.swid.builder.VersionScheme;
40 import gov.nist.secauto.swid.builder.resource.AbstractResourceCollectionBuilder;
41 import gov.nist.secauto.swid.builder.resource.EvidenceBuilder;
42 import gov.nist.secauto.swid.builder.resource.HashAlgorithm;
43 import gov.nist.secauto.swid.builder.resource.PathRelativizer;
44 import gov.nist.secauto.swid.builder.resource.PayloadBuilder;
45 import gov.nist.secauto.swid.builder.resource.ResourceBuilder;
46 import gov.nist.secauto.swid.builder.resource.ResourceCollectionEntryGenerator;
47 import gov.nist.secauto.swid.builder.resource.file.AbstractFileSystemItemBuilder;
48 import gov.nist.secauto.swid.builder.resource.file.DirectoryBuilder;
49 import gov.nist.secauto.swid.builder.resource.file.FileBuilder;
50 import gov.nist.secauto.swid.builder.resource.firmware.FirmwareBuilder;
51
52 import java.io.IOException;
53 import java.io.OutputStream;
54 import java.time.ZonedDateTime;
55 import java.util.List;
56 import java.util.Map;
57
58 public abstract class AbstractJsonOutputHandler extends JsonSupport implements OutputHandler {
59
60
61
62 public static final long TAG_ID_FIELD = 0L;
63
64
65
66
67 public static final long SWID_NAME_FIELD = 1L;
68 public static final long ENTITY_FIELD = 2L;
69 public static final long EVIDENCE_FIELD = 3L;
70 public static final long LINK_FIELD = 4L;
71 public static final long SOFTWARE_META_FIELD = 5L;
72 public static final long PAYLOAD_FIELD = 6L;
73 public static final long CORPUS_FIELD = 8L;
74 public static final long PATCH_FIELD = 9L;
75 public static final long MEDIA_FIELD = 10L;
76 public static final long SUPPLEMENTAL_FIELD = 11L;
77 public static final long TAG_VERSION_FIELD = 12L;
78 public static final long SOFTWARE_VERSION_FIELD = 13L;
79 public static final long VERSION_SCHEME_FIELD = 14L;
80 public static final long LANG_FIELD = 15L;
81 public static final long DIRECTORY_FIELD = 16L;
82 public static final long FILE_FIELD = 17L;
83 public static final long PROCESS_FIELD = 18L;
84 public static final long RESOURCE_FIELD = 19L;
85
86
87
88
89 public static final long SIZE_FIELD = 20L;
90 public static final long FILE_VERSION_FIELD = 21L;
91
92
93
94
95 public static final long KEY_FIELD = 22L;
96 public static final long LOCATION_FIELD = 23L;
97 public static final long FS_NAME_FIELD = 24L;
98 public static final long ROOT_FIELD = 25L;
99 public static final long PATH_ELEMENTS_FIELD = 26L;
100 public static final long PROCESS_NAME_FIELD = 27L;
101 public static final long PID_FIELD = 28L;
102 public static final long TYPE_FIELD = 29L;
103 public static final long ENTITY_NAME_FIELD = 31L;
104 public static final long REG_ID_FIELD = 32L;
105
106
107
108
109 public static final long ROLE_FIELD = 33L;
110 public static final long THUMBPRINT_FIELD = 34L;
111
112 public static final long DATE_FIELD = 35L;
113 public static final long DEVICE_ID_FIELD = 36L;
114 public static final long ARTIFACT_FIELD = 37L;
115 public static final long HREF_FIELD = 38L;
116 public static final long OWNERSHIP_FIELD = 39L;
117 public static final long REL_FIELD = 40L;
118 public static final long MEDIA_TYPE_FIELD = 41L;
119 public static final long USE_FIELD = 42L;
120
121 public static final long ACTIVATION_STATUS_FIELD = 43L;
122 public static final long CHANNEL_TYPE_FIELD = 44L;
123
124 public static final long COLLOQUIAL_VERSION_FIELD = 45L;
125
126 public static final long DESCRIPTION_FIELD = 46L;
127
128 public static final long EDITION_FIELD = 47L;
129
130 public static final long ENTITLEMENT_DATA_REQUIRED_FIELD = 48L;
131
132 public static final long ENTITLEMENT_KEY_FIELD = 49L;
133
134 public static final long GENERATOR_FIELD = 50L;
135
136 public static final long PERSISTENT_ID_FIELD = 51L;
137
138 public static final long PRODUCT_FIELD = 52L;
139
140 public static final long PRODUCT_FAMILY_FIELD = 53L;
141
142 public static final long REVISION_FIELD = 54L;
143
144 public static final long SUMMARY_FIELD = 55L;
145
146 public static final long UNSPSC_CODE_FIELD = 56L;
147
148 public static final long UNSPSC_VERSION_FIELD = 57L;
149
150 public static final long HASH_FIELD = 7L;
151
152
153
154
155 public static final long FIRMWARE_FIELD = 59L;
156
157 private final JsonFactory jsonFactory;
158
159 public AbstractJsonOutputHandler(JsonFactory jsonFactory) {
160 this.jsonFactory = jsonFactory;
161 }
162
163 protected abstract void writeRole(JsonGenerator generator, Role role) throws IOException;
164
165 protected abstract void writeVersionScheme(JsonGenerator generator, VersionScheme versionScheme) throws IOException;
166
167
168
169
170 public JsonFactory getJsonFactory() {
171 return jsonFactory;
172 }
173
174 protected JsonGenerator newGenerator(OutputStream os) throws IOException {
175
176 JsonFactory factory = getJsonFactory();
177
178 JsonGenerator generator = factory.createGenerator(os);
179 return generator;
180 }
181
182 @Override
183 public void write(SWIDBuilder builder, OutputStream os) throws IOException, ValidationException {
184 builder.validate();
185
186 JsonGenerator generator = newGenerator(os);
187
188 build(generator, builder);
189
190 generator.close();
191 }
192
193 protected void build(JsonGenerator generator, SWIDBuilder builder) throws IOException {
194 generator.writeStartObject();
195
196 buildGlobalAttributes(generator, builder);
197
198
199 writeTextField(generator, TAG_ID_FIELD, builder.getTagId());
200
201 writeIntegerField(generator, TAG_VERSION_FIELD, builder.getTagVersion());
202
203 writeTextField(generator, SWID_NAME_FIELD, builder.getName());
204 if (builder.getVersion() != null) {
205 writeTextField(generator, SOFTWARE_VERSION_FIELD, builder.getVersion());
206 }
207 VersionScheme versionScheme = builder.getVersionScheme();
208 if (versionScheme != null) {
209 writeField(generator, VERSION_SCHEME_FIELD);
210 writeVersionScheme(generator, versionScheme);
211 }
212
213
214 switch (builder.getTagType()) {
215 case PRIMARY:
216 break;
217 case CORPUS:
218 writeBooleanField(generator, CORPUS_FIELD, true);
219 break;
220 case PATCH:
221 writeBooleanField(generator, PATCH_FIELD, true);
222 break;
223 case SUPPLEMENTAL:
224 writeBooleanField(generator, SUPPLEMENTAL_FIELD, true);
225 break;
226 default:
227 throw new IllegalStateException("tagType: " + builder.getTagType().toString());
228 }
229
230
231
232 writeField(generator, ENTITY_FIELD);
233 List<EntityBuilder> entities = builder.getEntities();
234 if (entities.size() > 1) {
235 generator.writeStartArray();
236 }
237
238 for (EntityBuilder entity : builder.getEntities()) {
239 build(generator, entity);
240 }
241
242 if (entities.size() > 1) {
243 generator.writeEndArray();
244 }
245
246
247 EvidenceBuilder evidence = builder.getEvidence();
248 if (evidence != null) {
249 writeField(generator, EVIDENCE_FIELD);
250 build(generator, evidence);
251 }
252
253 List<LinkBuilder> links = builder.getLinks();
254 if (!links.isEmpty()) {
255 writeField(generator, LINK_FIELD);
256 if (links.size() > 1) {
257 generator.writeStartArray();
258 }
259 for (LinkBuilder link : links) {
260 build(generator, link);
261 }
262 if (links.size() > 1) {
263 generator.writeEndArray();
264 }
265 }
266
267 List<MetaBuilder> metas = builder.getMetas();
268 if (!links.isEmpty()) {
269 writeField(generator, SOFTWARE_META_FIELD);
270 if (metas.size() > 1) {
271 generator.writeStartArray();
272 }
273 for (MetaBuilder meta : metas) {
274 buildMeta(generator, meta);
275 }
276 if (metas.size() > 1) {
277 generator.writeEndArray();
278 }
279 }
280
281 PayloadBuilder payload = builder.getPayload();
282 if (payload != null) {
283 writeField(generator, PAYLOAD_FIELD);
284 buildPayload(generator, payload);
285 }
286
287
288
289
290 generator.writeEndObject();
291 }
292
293 protected void build(JsonGenerator generator, EntityBuilder builder) throws IOException {
294
295
296 generator.writeStartObject();
297
298 buildGlobalAttributes(generator, builder);
299
300 writeTextField(generator, ENTITY_NAME_FIELD, builder.getName());
301 if (builder.getRegid() != null) {
302 writeTextField(generator, REG_ID_FIELD, builder.getRegid());
303 }
304
305 List<Role> roles = builder.getRoles();
306 writeField(generator, ROLE_FIELD);
307 if (roles.size() > 1) {
308 generator.writeStartArray();
309 }
310 for (Role role : roles) {
311 writeRole(generator, role);
312 }
313 if (roles.size() > 1) {
314 generator.writeEndArray();
315 }
316
317 if (builder.getThumbprint() != null) {
318 writeTextField(generator, THUMBPRINT_FIELD, builder.getThumbprint());
319 }
320
321
322
323
324
325
326
327 generator.writeEndObject();
328 }
329
330 private void build(JsonGenerator generator, EvidenceBuilder builder) throws IOException {
331
332
333 generator.writeStartObject();
334
335 buildGlobalAttributes(generator, builder);
336
337 buildResourceCollection(generator, builder);
338
339 ZonedDateTime date = builder.getDate();
340 if (date != null) {
341 writeDateTimeField(generator, DATE_FIELD, date);
342 }
343
344 if (builder.getDeviceId() != null) {
345 writeTextField(generator, DEVICE_ID_FIELD, builder.getDeviceId());
346 }
347
348
349 generator.writeEndObject();
350 }
351
352 private void build(JsonGenerator generator, LinkBuilder builder) throws IOException {
353
354
355 generator.writeStartObject();
356
357 buildGlobalAttributes(generator, builder);
358
359
360 writeTextField(generator, HREF_FIELD, builder.getHref().toString());
361 writeTextField(generator, REL_FIELD, builder.getRel());
362
363
364 if (builder.getArtifact() != null) {
365 writeTextField(generator, ARTIFACT_FIELD, builder.getArtifact());
366 }
367 if (builder.getMedia() != null) {
368 writeTextField(generator, MEDIA_FIELD, builder.getMedia());
369 }
370 if (builder.getOwnership() != null) {
371 writeTextField(generator, OWNERSHIP_FIELD, builder.getOwnership().toString());
372 }
373 if (builder.getMediaType() != null) {
374 writeTextField(generator, MEDIA_TYPE_FIELD, builder.getMediaType());
375 }
376 if (builder.getUse() != null) {
377 writeTextField(generator, USE_FIELD, builder.getUse().toString());
378 }
379
380
381 generator.writeEndObject();
382 }
383
384 private void buildMeta(JsonGenerator generator, MetaBuilder builder) throws IOException {
385
386
387 generator.writeStartObject();
388
389 buildGlobalAttributes(generator, builder);
390
391 buildAttribute(generator, ACTIVATION_STATUS_FIELD, builder.getActivationStatus());
392 buildAttribute(generator, CHANNEL_TYPE_FIELD, builder.getChannelType());
393 buildAttribute(generator, COLLOQUIAL_VERSION_FIELD, builder.getColloquialVersion());
394 buildAttribute(generator, DESCRIPTION_FIELD, builder.getDescription());
395 buildAttribute(generator, EDITION_FIELD, builder.getEdition());
396 buildAttribute(generator, ENTITLEMENT_DATA_REQUIRED_FIELD, builder.getEntitlementDataRequired());
397 buildAttribute(generator, ENTITLEMENT_KEY_FIELD, builder.getEntitlementKey());
398 buildAttribute(generator, GENERATOR_FIELD, builder.getGenerator());
399 buildAttribute(generator, PERSISTENT_ID_FIELD, builder.getPersistentId());
400 buildAttribute(generator, PRODUCT_FIELD, builder.getProductBaseName());
401 buildAttribute(generator, PRODUCT_FAMILY_FIELD, builder.getProductFamily());
402 buildAttribute(generator, REVISION_FIELD, builder.getRevision());
403 buildAttribute(generator, SUMMARY_FIELD, builder.getSummary());
404 buildAttribute(generator, UNSPSC_CODE_FIELD, builder.getUnspscCode());
405 buildAttribute(generator, UNSPSC_VERSION_FIELD, builder.getUnspscVersion());
406
407
408 generator.writeEndObject();
409 }
410
411 private void buildAttribute(JsonGenerator generator, long fieldId, String value) throws IOException {
412 if (value != null) {
413 writeTextField(generator, fieldId, value);
414 }
415 }
416
417 private void buildPayload(JsonGenerator generator, PayloadBuilder builder) throws IOException {
418
419
420 generator.writeStartObject();
421
422 buildGlobalAttributes(generator, builder);
423
424 buildResourceCollection(generator, builder);
425
426
427 generator.writeEndObject();
428 }
429
430 private <E extends AbstractResourceCollectionBuilder<E>> void buildResourceCollection(JsonGenerator generator,
431 AbstractResourceCollectionBuilder<E> builder) throws IOException {
432 buildGlobalAttributes(generator, builder);
433
434 JsonResourceCollectionEntryGenerator creator = new JsonResourceCollectionEntryGenerator();
435 {
436 List<DirectoryBuilder> directories = builder.getResources(DirectoryBuilder.class);
437 if (!directories.isEmpty()) {
438 writeDirectories(generator, directories, creator);
439 }
440 }
441 {
442 List<FileBuilder> files = builder.getResources(FileBuilder.class);
443 if (!files.isEmpty()) {
444 writeFiles(generator, files, creator);
445 }
446 }
447 {
448 List<FirmwareBuilder> firmwares = builder.getResources(FirmwareBuilder.class);
449 if (!firmwares.isEmpty()) {
450 writeFirmware(generator, firmwares, creator);
451 }
452 }
453 }
454
455 private void writeDirectories(JsonGenerator generator, List<DirectoryBuilder> directories,
456 JsonResourceCollectionEntryGenerator creator) throws IOException {
457 writeField(generator, DIRECTORY_FIELD);
458 writeResources(generator, directories, creator);
459 }
460
461 private void writeFiles(JsonGenerator generator, List<FileBuilder> files,
462 JsonResourceCollectionEntryGenerator creator) throws IOException {
463 writeField(generator, FILE_FIELD);
464 writeResources(generator, files, creator);
465 }
466
467 private void writeFirmware(JsonGenerator generator, List<FirmwareBuilder> firmwares,
468 JsonResourceCollectionEntryGenerator creator) throws IOException {
469 writeField(generator, FIRMWARE_FIELD);
470 writeResources(generator, firmwares, creator);
471 }
472
473 private void writeResources(JsonGenerator generator, List<? extends ResourceBuilder> resources,
474 JsonResourceCollectionEntryGenerator creator) throws IOException {
475
476 if (resources.size() > 1) {
477 generator.writeStartArray();
478 }
479 for (ResourceBuilder builder : resources) {
480 builder.accept(generator, creator);
481 }
482 if (resources.size() > 1) {
483 generator.writeEndArray();
484 }
485 }
486
487 private void writeHash(JsonGenerator generator, HashAlgorithm algorithm, byte[] bytes) throws IOException {
488 writeField(generator, HASH_FIELD);
489 generator.writeStartArray();
490 generator.writeNumber(algorithm.getIndex());
491 generator.writeBinary(bytes);
492 generator.writeEndArray();
493 }
494
495 private <E extends AbstractLanguageSpecificBuilder<E>> void buildGlobalAttributes(JsonGenerator generator,
496 AbstractLanguageSpecificBuilder<E> builder) throws IOException {
497 String language = builder.getLanguage();
498 if (language != null) {
499 writeTextField(generator, LANG_FIELD, language);
500 }
501 }
502
503 private class JsonResourceCollectionEntryGenerator implements ResourceCollectionEntryGenerator<JsonGenerator> {
504
505 public JsonResourceCollectionEntryGenerator() {
506 }
507
508 @Override
509 public void generate(JsonGenerator parent, DirectoryBuilder builder) {
510 try {
511 parent.writeStartObject();
512
513 buildGlobalAttributes(parent, builder);
514
515 buildFileSystemItem(parent, builder);
516
517 {
518 List<DirectoryBuilder> directories = builder.getResources(DirectoryBuilder.class);
519 if (!directories.isEmpty()) {
520 writeDirectories(parent, directories, this);
521 }
522 }
523 {
524 List<FileBuilder> files = builder.getResources(FileBuilder.class);
525 if (!files.isEmpty()) {
526 writeFiles(parent, files, this);
527 }
528 }
529
530 parent.writeEndObject();
531 } catch (IOException ex) {
532 throw new RuntimeException(ex);
533 }
534 }
535
536 @Override
537 public void generate(JsonGenerator parent, FileBuilder builder) {
538
539 try {
540 parent.writeStartObject();
541
542 buildGlobalAttributes(parent, builder);
543
544 buildFileSystemItem(parent, builder);
545
546 if (builder.getSize() != null) {
547 writeLongField(parent, SIZE_FIELD, builder.getSize());
548 }
549 if (builder.getVersion() != null) {
550 writeTextField(parent, SOFTWARE_VERSION_FIELD, builder.getVersion());
551 }
552
553 Map<HashAlgorithm, byte[]> hashMap = builder.getHashAlgorithmToValueMap();
554 if (!hashMap.isEmpty()) {
555 if (hashMap.size() > 1) {
556 throw new UnsupportedOperationException("Only a single hash value is allowed");
557 }
558
559 Map.Entry<HashAlgorithm, byte[]> entry = hashMap.entrySet().iterator().next();
560 writeHash(parent, entry.getKey(), entry.getValue());
561 }
562
563
564 parent.writeEndObject();
565 } catch (IOException ex) {
566 throw new RuntimeException(ex);
567 }
568 }
569
570 @Override
571 public void generate(JsonGenerator generator, FirmwareBuilder builder) {
572 new CBORFirmwareOutputHandler().generate(generator, builder);
573 }
574
575 private <E extends AbstractFileSystemItemBuilder<E>> void buildFileSystemItem(JsonGenerator parent,
576 AbstractFileSystemItemBuilder<E> builder) throws IOException {
577
578
579
580 if (builder.getKey() != null) {
581 writeBooleanField(parent, KEY_FIELD, builder.getKey());
582 }
583
584 if (builder.getRoot() != null) {
585 writeTextField(parent, ROOT_FIELD, builder.getRoot());
586 }
587
588 List<String> location = builder.getLocation();
589 if (location != null && !location.isEmpty()) {
590 writeTextField(parent, LOCATION_FIELD, PathRelativizer.toURI(location).toString());
591 }
592 writeTextField(parent, FS_NAME_FIELD, builder.getName());
593 }
594 }
595 }