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 gov.nist.secauto.swid.builder.AbstractLanguageSpecificBuilder;
30 import gov.nist.secauto.swid.builder.EntityBuilder;
31 import gov.nist.secauto.swid.builder.LinkBuilder;
32 import gov.nist.secauto.swid.builder.MetaBuilder;
33 import gov.nist.secauto.swid.builder.Role;
34 import gov.nist.secauto.swid.builder.SWIDBuilder;
35 import gov.nist.secauto.swid.builder.ValidationException;
36 import gov.nist.secauto.swid.builder.resource.AbstractResourceBuilder;
37 import gov.nist.secauto.swid.builder.resource.AbstractResourceCollectionBuilder;
38 import gov.nist.secauto.swid.builder.resource.EvidenceBuilder;
39 import gov.nist.secauto.swid.builder.resource.HashAlgorithm;
40 import gov.nist.secauto.swid.builder.resource.HashUtils;
41 import gov.nist.secauto.swid.builder.resource.PathRelativizer;
42 import gov.nist.secauto.swid.builder.resource.PayloadBuilder;
43 import gov.nist.secauto.swid.builder.resource.ResourceBuilder;
44 import gov.nist.secauto.swid.builder.resource.ResourceCollectionEntryGenerator;
45 import gov.nist.secauto.swid.builder.resource.file.AbstractFileSystemItemBuilder;
46 import gov.nist.secauto.swid.builder.resource.file.DirectoryBuilder;
47 import gov.nist.secauto.swid.builder.resource.file.FileBuilder;
48 import gov.nist.secauto.swid.builder.resource.firmware.FirmwareBuilder;
49
50 import org.jdom2.Document;
51 import org.jdom2.Element;
52 import org.jdom2.Namespace;
53 import org.jdom2.output.Format;
54 import org.jdom2.output.XMLOutputter;
55
56 import java.io.IOException;
57 import java.io.OutputStream;
58 import java.time.ZonedDateTime;
59 import java.time.format.DateTimeFormatter;
60 import java.util.List;
61 import java.util.Map;
62
63 public class XMLOutputHandler implements OutputHandler {
64 public static final Namespace SWID_NAMESPACE
65 = Namespace.getNamespace("http://standards.iso.org/iso/19770/-2/2015/schema.xsd");
66
67 private final Format format;
68
69 public XMLOutputHandler() {
70 this(Format.getPrettyFormat());
71 }
72
73 public XMLOutputHandler(Format format) {
74 this.format = format;
75 }
76
77 @Override
78 public void write(SWIDBuilder builder, OutputStream os) throws IOException, ValidationException {
79 XMLOutputter out = new XMLOutputter(format);
80 out.output(generateXML(builder), os);
81 }
82
83
84
85
86
87
88
89
90
91
92 public Document generateXML(SWIDBuilder builder) throws ValidationException {
93 builder.validate();
94
95 Document retval = buildDocument(builder);
96 return retval;
97 }
98
99 protected Document buildDocument(SWIDBuilder builder) {
100 return new Document(build(builder));
101 }
102
103 protected Element build(SWIDBuilder builder) {
104 Element element = new Element("SoftwareIdentity", SWID_NAMESPACE);
105
106 buildAbstractLanguageSpecificBuilder(builder, element);
107
108
109 element.setAttribute("name", builder.getName());
110 element.setAttribute("tagId", builder.getTagId());
111
112
113 switch (builder.getTagType()) {
114 case PRIMARY:
115 break;
116 case CORPUS:
117 element.setAttribute("corpus", Boolean.TRUE.toString());
118 break;
119 case PATCH:
120 element.setAttribute("patch", Boolean.TRUE.toString());
121 break;
122 case SUPPLEMENTAL:
123 element.setAttribute("supplemental", Boolean.TRUE.toString());
124 break;
125 default:
126 throw new IllegalStateException("tagType: " + builder.getTagType().toString());
127 }
128
129 element.setAttribute("tagVersion", builder.getTagVersion().toString());
130
131 buildAttribute("version", builder.getVersion(), element);
132 buildAttribute("versionScheme", builder.getVersionScheme().getName(), element);
133
134
135
136 for (EntityBuilder entity : builder.getEntities()) {
137 element.addContent(build(entity));
138 }
139
140
141 EvidenceBuilder evidence = builder.getEvidence();
142 if (evidence != null) {
143 element.addContent(build(evidence));
144 }
145
146 for (LinkBuilder link : builder.getLinks()) {
147 element.addContent(build(link));
148 }
149
150 for (MetaBuilder meta : builder.getMetas()) {
151 element.addContent(build(meta));
152 }
153
154 PayloadBuilder payload = builder.getPayload();
155 if (payload != null) {
156 element.addContent(build(payload));
157 }
158
159 return element;
160 }
161
162 protected Element build(EntityBuilder builder) {
163 Element element = new Element("Entity", SWID_NAMESPACE);
164
165 buildAbstractLanguageSpecificBuilder(builder, element);
166
167
168 element.setAttribute("name", builder.getName());
169
170 StringBuilder sb = null;
171 for (Role role : builder.getRoles()) {
172 if (sb == null) {
173 sb = new StringBuilder();
174 } else {
175 sb.append(' ');
176 }
177 sb.append(role.getName());
178 }
179 if (sb != null) {
180 element.setAttribute("role", sb.toString());
181 }
182
183
184 buildAttribute("regid", builder.getRegid(), element);
185 buildAttribute("thumbprint", builder.getThumbprint(), element);
186
187 return element;
188 }
189
190 protected Element build(EvidenceBuilder builder) {
191 Element element = new Element("Evidence", SWID_NAMESPACE);
192
193 buildAbstractResourceCollectionBuilder(builder, element);
194
195 ZonedDateTime date = builder.getDate();
196 if (date != null) {
197 element.setAttribute("date", date.format(DateTimeFormatter.ISO_OFFSET_DATE_TIME));
198 }
199 buildAttribute("deviceId", builder.getDeviceId(), element);
200
201 return element;
202 }
203
204 protected Element build(LinkBuilder builder) {
205 Element element = new Element("Link", SWID_NAMESPACE);
206
207 buildAbstractLanguageSpecificBuilder(builder, element);
208
209
210 element.setAttribute("href", builder.getHref().toString());
211 element.setAttribute("rel", builder.getRel());
212
213
214 buildAttribute("artifact", builder.getArtifact(), element);
215 buildAttribute("media", builder.getMedia(), element);
216 buildAttribute("ownership", builder.getOwnership(), element);
217 buildAttribute("type", builder.getMediaType(), element);
218 buildAttribute("use", builder.getUse(), element);
219
220 return element;
221 }
222
223 protected Element build(MetaBuilder builder) {
224 Element element = new Element("Meta", SWID_NAMESPACE);
225
226 buildAttribute("activationStatus", builder.getActivationStatus(), element);
227 buildAttribute("channelType", builder.getChannelType(), element);
228 buildAttribute("colloquialVersion", builder.getColloquialVersion(), element);
229 buildAttribute("description", builder.getDescription(), element);
230 buildAttribute("edition", builder.getEdition(), element);
231 buildAttribute("entitlementDataRequired", builder.getEntitlementDataRequired(), element);
232 buildAttribute("entitlementKey", builder.getEntitlementKey(), element);
233 buildAttribute("generator", builder.getGenerator(), element);
234 buildAttribute("persistentId", builder.getPersistentId(), element);
235 buildAttribute("product", builder.getProductBaseName(), element);
236 buildAttribute("productFamily", builder.getProductFamily(), element);
237 buildAttribute("revision", builder.getRevision(), element);
238 buildAttribute("summary", builder.getSummary(), element);
239 buildAttribute("unspscCode", builder.getUnspscCode(), element);
240 buildAttribute("unspscVersion", builder.getUnspscVersion(), element);
241
242 return element;
243 }
244
245 protected Element build(PayloadBuilder builder) {
246 Element element = new Element("Payload", SWID_NAMESPACE);
247
248 buildAbstractResourceCollectionBuilder(builder, element);
249
250 return element;
251 }
252
253 private static <E extends AbstractResourceCollectionBuilder<E>> void
254 buildAbstractResourceCollectionBuilder(AbstractResourceCollectionBuilder<E> builder, Element element) {
255 buildAbstractLanguageSpecificBuilder(builder, element);
256
257 XMLResourceCollectionEntryGenerator creator = new XMLResourceCollectionEntryGenerator();
258 for (ResourceBuilder resourceBuilder : builder.getResources()) {
259 resourceBuilder.accept(element, creator);
260 }
261 }
262
263 private static <E extends AbstractLanguageSpecificBuilder<E>> void
264 buildAbstractLanguageSpecificBuilder(AbstractLanguageSpecificBuilder<E> builder, Element element) {
265 String language = builder.getLanguage();
266 if (language != null) {
267 element.setAttribute("lang", language, Namespace.XML_NAMESPACE);
268 }
269 }
270
271 private static void buildAttribute(String attributeName, String value, Element element) {
272 if (value != null) {
273 element.setAttribute(attributeName, value);
274 }
275 }
276
277 private static void buildAttribute(String attributeName, Object value, Element element) {
278 if (value != null) {
279 element.setAttribute(attributeName, value.toString());
280 }
281 }
282
283 private static class XMLResourceCollectionEntryGenerator implements ResourceCollectionEntryGenerator<Element> {
284 public XMLResourceCollectionEntryGenerator() {
285 }
286
287 @Override
288 public void generate(Element parent, DirectoryBuilder builder) {
289 Element element = new Element("Directory", XMLOutputHandler.SWID_NAMESPACE);
290 parent.addContent(element);
291
292 buildAbstractFileSystemItem(builder, element);
293
294 for (ResourceBuilder child : builder.getResources()) {
295 child.accept(element, this);
296 }
297 }
298
299 @Override
300 public void generate(Element parent, FileBuilder builder) {
301
302 Element element = new Element("File", XMLOutputHandler.SWID_NAMESPACE);
303 parent.addContent(element);
304
305 buildAbstractFileSystemItem(builder, element);
306
307 XMLOutputHandler.buildAttribute("size", builder.getSize(), element);
308 XMLOutputHandler.buildAttribute("version", builder.getVersion(), element);
309
310 Element rootElement = parent;
311 while (rootElement.getParentElement() != null) {
312 rootElement = rootElement.getParentElement();
313 }
314 for (Map.Entry<HashAlgorithm, byte[]> entry : builder.getHashAlgorithmToValueMap().entrySet()) {
315 HashAlgorithm algorithm = entry.getKey();
316 byte[] hashValue = entry.getValue();
317
318 Namespace ns = Namespace.getNamespace(algorithm.getName(), algorithm.getNamespace());
319 Namespace nsOld = rootElement.getNamespace(ns.getPrefix());
320
321 if (nsOld == null) {
322 rootElement.addNamespaceDeclaration(ns);
323 } else if (!nsOld.getURI().equals(ns.getURI())) {
324 element.addNamespaceDeclaration(ns);
325 }
326 element.setAttribute("hash", HashUtils.toHexString(hashValue), ns);
327 }
328 }
329
330 @Override
331 public void generate(Element parent, FirmwareBuilder firmwareBuilder) {
332 throw new UnsupportedOperationException("firmware is not supported by the XML SWID format");
333 }
334
335 private static <E extends AbstractFileSystemItemBuilder<E>> void
336 buildAbstractFileSystemItem(AbstractFileSystemItemBuilder<E> builder, Element element) {
337 buildAbstractResourceBuilder(builder, element);
338
339 XMLOutputHandler.buildAttribute("root", builder.getRoot(), element);
340 List<String> location = builder.getLocation();
341 if (location != null && !location.isEmpty()) {
342 element.setAttribute("location", PathRelativizer.toURI(location).toString());
343 }
344 XMLOutputHandler.buildAttribute("name", builder.getName(), element);
345 XMLOutputHandler.buildAttribute("key", builder.getKey(), element);
346 }
347
348 private static <E extends AbstractResourceBuilder<E>> void
349 buildAbstractResourceBuilder(AbstractResourceBuilder<E> builder, Element element) {
350 XMLOutputHandler.buildAbstractLanguageSpecificBuilder(builder, element);
351 }
352 }
353 }