• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2016 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.google.attestationexample;
18 
19 import android.security.keystore.KeyProperties;
20 import android.util.Log;
21 
22 import com.google.common.base.Joiner;
23 import com.google.common.collect.ImmutableMap;
24 import com.google.common.collect.ImmutableSet;
25 import com.google.common.collect.Lists;
26 
27 import org.bouncycastle.asn1.ASN1Encodable;
28 import org.bouncycastle.asn1.ASN1Primitive;
29 import org.bouncycastle.asn1.ASN1Sequence;
30 import org.bouncycastle.asn1.ASN1SequenceParser;
31 import org.bouncycastle.asn1.ASN1TaggedObject;
32 import org.bouncycastle.util.encoders.Hex;
33 
34 import java.io.IOException;
35 import java.security.cert.CertificateParsingException;
36 import java.text.DateFormat;
37 import java.util.Collection;
38 import java.util.Date;
39 import java.util.List;
40 import java.util.Set;
41 
42 import static com.google.common.base.Functions.forMap;
43 import static com.google.common.collect.Collections2.transform;
44 
45 public class AuthorizationList {
46     // Algorithm values.
47     public static final int KM_ALGORITHM_RSA = 1;
48     public static final int KM_ALGORITHM_EC = 3;
49 
50     // EC Curves
51     public static final int KM_EC_CURVE_P224 = 0;
52     public static final int KM_EC_CURVE_P256 = 1;
53     public static final int KM_EC_CURVE_P384 = 2;
54     public static final int KM_EC_CURVE_P521 = 3;
55 
56     // Padding modes.
57     public static final int KM_PAD_NONE = 1;
58     public static final int KM_PAD_RSA_OAEP = 2;
59     public static final int KM_PAD_RSA_PSS = 3;
60     public static final int KM_PAD_RSA_PKCS1_1_5_ENCRYPT = 4;
61     public static final int KM_PAD_RSA_PKCS1_1_5_SIGN = 5;
62 
63     // Digest modes.
64     public static final int KM_DIGEST_NONE = 0;
65     public static final int KM_DIGEST_MD5 = 1;
66     public static final int KM_DIGEST_SHA1 = 2;
67     public static final int KM_DIGEST_SHA_2_224 = 3;
68     public static final int KM_DIGEST_SHA_2_256 = 4;
69     public static final int KM_DIGEST_SHA_2_384 = 5;
70     public static final int KM_DIGEST_SHA_2_512 = 6;
71 
72     // Key origins.
73     public static final int KM_ORIGIN_GENERATED = 0;
74     public static final int KM_ORIGIN_IMPORTED = 2;
75     public static final int KM_ORIGIN_UNKNOWN = 3;
76 
77     // Operation Purposes.
78     public static final int KM_PURPOSE_ENCRYPT = 0;
79     public static final int KM_PURPOSE_DECRYPT = 1;
80     public static final int KM_PURPOSE_SIGN = 2;
81     public static final int KM_PURPOSE_VERIFY = 3;
82     public static final int KM_PURPOSE_DERIVE_KEY = 4;
83     public static final int KM_PURPOSE_WRAP_KEY = 5;
84 
85     // User authenticators.
86     public static final int HW_AUTH_PASSWORD = 1 << 0;
87     public static final int HW_AUTH_FINGERPRINT = 1 << 1;
88 
89     // Keymaster tag classes
90     private static final int KM_ENUM = 1 << 28;
91     private static final int KM_ENUM_REP = 2 << 28;
92     private static final int KM_UINT = 3 << 28;
93     private static final int KM_ULONG = 5 << 28;
94     private static final int KM_DATE = 6 << 28;
95     private static final int KM_BOOL = 7 << 28;
96     private static final int KM_BYTES = 9 << 28;
97 
98     // Tag class removal mask
99     private static final int KEYMASTER_TAG_TYPE_MASK = 0x0FFFFFFF;
100 
101     // Keymaster tags
102     private static final int KM_TAG_PURPOSE = KM_ENUM_REP | 1;
103     private static final int KM_TAG_ALGORITHM = KM_ENUM | 2;
104     private static final int KM_TAG_KEY_SIZE = KM_UINT | 3;
105     private static final int KM_TAG_DIGEST = KM_ENUM_REP | 5;
106     private static final int KM_TAG_PADDING = KM_ENUM_REP | 6;
107     private static final int KM_TAG_EC_CURVE = KM_ENUM | 10;
108     private static final int KM_TAG_RSA_PUBLIC_EXPONENT = KM_ULONG | 200;
109     private static final int KM_TAG_ACTIVE_DATETIME = KM_DATE | 400;
110     private static final int KM_TAG_ORIGINATION_EXPIRE_DATETIME = KM_DATE | 401;
111     private static final int KM_TAG_USAGE_EXPIRE_DATETIME = KM_DATE | 402;
112     private static final int KM_TAG_NO_AUTH_REQUIRED = KM_BOOL | 503;
113     private static final int KM_TAG_USER_AUTH_TYPE = KM_ENUM | 504;
114     private static final int KM_TAG_ALLOW_WHILE_ON_BODY = KM_BOOL | 506;
115     private static final int KM_TAG_AUTH_TIMEOUT = KM_UINT | 505;
116     private static final int KM_TAG_ALL_APPLICATIONS = KM_BOOL | 600;
117     private static final int KM_TAG_APPLICATION_ID = KM_BYTES | 601;
118     private static final int KM_TAG_CREATION_DATETIME = KM_DATE | 701;
119     private static final int KM_TAG_ORIGIN = KM_ENUM | 702;
120     private static final int KM_TAG_ROLLBACK_RESISTANT = KM_BOOL | 703;
121     private static final int KM_TAG_ROOT_OF_TRUST = KM_BYTES | 704;
122     private static final int KM_TAG_OS_VERSION = KM_UINT | 705;
123     private static final int KM_TAG_OS_PATCHLEVEL = KM_UINT | 706;
124     private static final int KM_TAG_ATTESTATION_APPLICATION_ID = KM_BYTES | 709;
125     private static final int KM_TAG_VENDOR_PATCHLEVEL = KM_UINT | 718;
126     private static final int KM_TAG_BOOT_PATCHLEVEL = KM_UINT | 719;
127     private static final int KM_TAG_MODULE_HASH = KM_BYTES | 724;
128 
129     // Map for converting padding values to strings
130     private static final ImmutableMap<Integer, String> paddingMap = ImmutableMap
131             .<Integer, String> builder()
132             .put(KM_PAD_NONE, "NONE")
133             .put(KM_PAD_RSA_OAEP, "OAEP")
134             .put(KM_PAD_RSA_PSS, "PSS")
135             .put(KM_PAD_RSA_PKCS1_1_5_ENCRYPT, "PKCS1 ENCRYPT")
136             .put(KM_PAD_RSA_PKCS1_1_5_SIGN, "PKCS1 SIGN")
137             .build();
138 
139     // Map for converting digest values to strings
140     private static final ImmutableMap<Integer, String> digestMap = ImmutableMap
141             .<Integer, String> builder()
142             .put(KM_DIGEST_NONE, "NONE")
143             .put(KM_DIGEST_MD5, "MD5")
144             .put(KM_DIGEST_SHA1, "SHA1")
145             .put(KM_DIGEST_SHA_2_224, "SHA224")
146             .put(KM_DIGEST_SHA_2_256, "SHA256")
147             .put(KM_DIGEST_SHA_2_384, "SHA384")
148             .put(KM_DIGEST_SHA_2_512, "SHA512")
149             .build();
150 
151     // Map for converting purpose values to strings
152     private static final ImmutableMap<Integer, String> purposeMap = ImmutableMap
153             .<Integer, String> builder()
154             .put(KM_PURPOSE_DECRYPT, "DECRYPT")
155             .put(KM_PURPOSE_ENCRYPT, "ENCRYPT")
156             .put(KM_PURPOSE_SIGN, "SIGN")
157             .put(KM_PURPOSE_VERIFY, "VERIFY")
158             .build();
159 
160     private Set<Integer> purposes;
161     private Integer algorithm;
162     private Integer keySize;
163     private Set<Integer> digests;
164     private Set<Integer> paddingModes;
165     private Integer ecCurve;
166     private Long rsaPublicExponent;
167     private Date activeDateTime;
168     private Date originationExpireDateTime;
169     private Date usageExpireDateTime;
170     private boolean noAuthRequired;
171     private Integer userAuthType;
172     private Integer authTimeout;
173     private boolean allowWhileOnBody;
174     private boolean allApplications;
175     private byte[] applicationId;
176     private Date creationDateTime;
177     private Integer origin;
178     private boolean rollbackResistant;
179     private RootOfTrust rootOfTrust;
180     private Integer osVersion;
181     private Integer osPatchLevel;
182     private Integer vendorPatchLevel;
183     private Integer bootPatchLevel;
184     private AttestationApplicationId attestationApplicationId;
185     private byte[] moduleHash;
186 
AuthorizationList(ASN1Encodable sequence)187     public AuthorizationList(ASN1Encodable sequence) throws CertificateParsingException {
188         if (!(sequence instanceof ASN1Sequence)) {
189             throw new CertificateParsingException("Expected sequence for authorization list, found "
190                     + sequence.getClass().getName());
191         }
192 
193         ASN1SequenceParser parser = ((ASN1Sequence) sequence).parser();
194         ASN1TaggedObject entry = parseAsn1TaggedObject(parser);
195         for (; entry != null; entry = parseAsn1TaggedObject(parser)) {
196             int tag = entry.getTagNo();
197             ASN1Primitive value = entry.getObject();
198             Log.i("Attestation", "Parsing tag: [" + tag + "], value: [" + value + "]");
199             switch (tag) {
200                 default:
201                     throw new CertificateParsingException("Unknown tag " + tag + " found");
202 
203                 case KM_TAG_PURPOSE & KEYMASTER_TAG_TYPE_MASK:
204                     purposes = Asn1Utils.getIntegersFromAsn1Set(value);
205                     break;
206                 case KM_TAG_ALGORITHM & KEYMASTER_TAG_TYPE_MASK:
207                     algorithm = Asn1Utils.getIntegerFromAsn1(value);
208                     break;
209                 case KM_TAG_KEY_SIZE & KEYMASTER_TAG_TYPE_MASK:
210                     keySize = Asn1Utils.getIntegerFromAsn1(value);
211                     Log.i("Attestation", "Found KEY SIZE, value: " + keySize);
212                     break;
213                 case KM_TAG_DIGEST & KEYMASTER_TAG_TYPE_MASK:
214                     digests = Asn1Utils.getIntegersFromAsn1Set(value);
215                     break;
216                 case KM_TAG_PADDING & KEYMASTER_TAG_TYPE_MASK:
217                     paddingModes = Asn1Utils.getIntegersFromAsn1Set(value);
218                     break;
219                 case KM_TAG_RSA_PUBLIC_EXPONENT & KEYMASTER_TAG_TYPE_MASK:
220                     rsaPublicExponent = Asn1Utils.getLongFromAsn1(value);
221                     break;
222                 case KM_TAG_NO_AUTH_REQUIRED & KEYMASTER_TAG_TYPE_MASK:
223                     noAuthRequired = true;
224                     break;
225                 case KM_TAG_CREATION_DATETIME & KEYMASTER_TAG_TYPE_MASK:
226                     creationDateTime = Asn1Utils.getDateFromAsn1(value);
227                     break;
228                 case KM_TAG_ORIGIN & KEYMASTER_TAG_TYPE_MASK:
229                     origin = Asn1Utils.getIntegerFromAsn1(value);
230                     break;
231                 case KM_TAG_OS_VERSION & KEYMASTER_TAG_TYPE_MASK:
232                     osVersion = Asn1Utils.getIntegerFromAsn1(value);
233                     break;
234                 case KM_TAG_OS_PATCHLEVEL & KEYMASTER_TAG_TYPE_MASK:
235                     osPatchLevel = Asn1Utils.getIntegerFromAsn1(value);
236                     break;
237                 case KM_TAG_VENDOR_PATCHLEVEL & KEYMASTER_TAG_TYPE_MASK:
238                     vendorPatchLevel = Asn1Utils.getIntegerFromAsn1(value);
239                     break;
240                 case KM_TAG_BOOT_PATCHLEVEL & KEYMASTER_TAG_TYPE_MASK:
241                     bootPatchLevel = Asn1Utils.getIntegerFromAsn1(value);
242                     break;
243                 case KM_TAG_ACTIVE_DATETIME & KEYMASTER_TAG_TYPE_MASK:
244                     activeDateTime = Asn1Utils.getDateFromAsn1(value);
245                     break;
246                 case KM_TAG_ORIGINATION_EXPIRE_DATETIME & KEYMASTER_TAG_TYPE_MASK:
247                     originationExpireDateTime = Asn1Utils.getDateFromAsn1(value);
248                     break;
249                 case KM_TAG_USAGE_EXPIRE_DATETIME & KEYMASTER_TAG_TYPE_MASK:
250                     usageExpireDateTime = Asn1Utils.getDateFromAsn1(value);
251                     break;
252                 case KM_TAG_APPLICATION_ID & KEYMASTER_TAG_TYPE_MASK:
253                     applicationId = Asn1Utils.getByteArrayFromAsn1(value);
254                     break;
255                 case KM_TAG_ROLLBACK_RESISTANT & KEYMASTER_TAG_TYPE_MASK:
256                     rollbackResistant = true;
257                     break;
258                 case KM_TAG_AUTH_TIMEOUT & KEYMASTER_TAG_TYPE_MASK:
259                     authTimeout = Asn1Utils.getIntegerFromAsn1(value);
260                     break;
261                 case KM_TAG_ALLOW_WHILE_ON_BODY & KEYMASTER_TAG_TYPE_MASK:
262                     allowWhileOnBody = true;
263                     break;
264                 case KM_TAG_EC_CURVE & KEYMASTER_TAG_TYPE_MASK:
265                     ecCurve = Asn1Utils.getIntegerFromAsn1(value);
266                     break;
267                 case KM_TAG_USER_AUTH_TYPE & KEYMASTER_TAG_TYPE_MASK:
268                     userAuthType = Asn1Utils.getIntegerFromAsn1(value);
269                     break;
270                 case KM_TAG_ROOT_OF_TRUST & KEYMASTER_TAG_TYPE_MASK:
271                     try {
272                         rootOfTrust = new RootOfTrust(value);
273                     } catch (CertificateParsingException e) {
274                         Log.e("AttestationFail", "Root of trust parsing failure" + e);
275                         rootOfTrust = null;
276                     }
277                     break;
278                 case KM_TAG_ATTESTATION_APPLICATION_ID & KEYMASTER_TAG_TYPE_MASK:
279                     attestationApplicationId = new AttestationApplicationId(Asn1Utils
280                             .getAsn1EncodableFromBytes(Asn1Utils.getByteArrayFromAsn1(value)));
281                     break;
282                 case KM_TAG_ALL_APPLICATIONS & KEYMASTER_TAG_TYPE_MASK:
283                     allApplications = true;
284                     break;
285                 case KM_TAG_MODULE_HASH & KEYMASTER_TAG_TYPE_MASK:
286                     moduleHash = Asn1Utils.getByteArrayFromAsn1(value);
287                     break;
288             }
289         }
290 
291     }
292 
algorithmToString(int algorithm)293     public static String algorithmToString(int algorithm) {
294         switch (algorithm) {
295             case KM_ALGORITHM_RSA:
296                 return "RSA";
297             case KM_ALGORITHM_EC:
298                 return "ECDSA";
299             default:
300                 return "Unknown";
301         }
302     }
303 
paddingModesToString(final Set<Integer> paddingModes)304     public static String paddingModesToString(final Set<Integer> paddingModes) {
305         return joinStrings(transform(paddingModes, forMap(paddingMap, "Unknown")));
306     }
307 
paddingModeToString(int paddingMode)308     public static String paddingModeToString(int paddingMode) {
309         return forMap(paddingMap, "Unknown").apply(paddingMode);
310     }
311 
digestsToString(Set<Integer> digests)312     public static String digestsToString(Set<Integer> digests) {
313         return joinStrings(transform(digests, forMap(digestMap, "Unknown")));
314     }
315 
digestToString(int digest)316     public static String digestToString(int digest) {
317         return forMap(digestMap, "Unknown").apply(digest);
318     }
319 
purposesToString(Set<Integer> purposes)320     public static String purposesToString(Set<Integer> purposes) {
321         return joinStrings(transform(purposes, forMap(purposeMap, "Unknown")));
322     }
323 
userAuthTypeToString(int userAuthType)324     public static String userAuthTypeToString(int userAuthType) {
325         List<String> types = Lists.newArrayList();
326         if ((userAuthType & HW_AUTH_FINGERPRINT) != 0)
327             types.add("Fingerprint");
328         if ((userAuthType & HW_AUTH_PASSWORD) != 0)
329             types.add("Password");
330         return joinStrings(types);
331     }
332 
originToString(int origin)333     public static String originToString(int origin) {
334         switch (origin) {
335             case KM_ORIGIN_GENERATED:
336                 return "Generated";
337             case KM_ORIGIN_IMPORTED:
338                 return "Imported";
339             case KM_ORIGIN_UNKNOWN:
340                 return "Unknown (KM0)";
341             default:
342                 return "Unknown";
343         }
344     }
345 
joinStrings(Collection<String> collection)346     private static String joinStrings(Collection<String> collection) {
347         return new StringBuilder()
348                 .append("[")
349                 .append(Joiner.on(", ").join(collection))
350                 .append("]")
351                 .toString();
352     }
353 
formatDate(Date date)354     private static String formatDate(Date date) {
355         return DateFormat.getDateTimeInstance().format(date);
356     }
357 
parseAsn1TaggedObject(ASN1SequenceParser parser)358     private static ASN1TaggedObject parseAsn1TaggedObject(ASN1SequenceParser parser)
359             throws CertificateParsingException {
360         ASN1Encodable asn1Encodable = parseAsn1Encodable(parser);
361         if (asn1Encodable == null || asn1Encodable instanceof ASN1TaggedObject) {
362             return (ASN1TaggedObject) asn1Encodable;
363         }
364         throw new CertificateParsingException(
365                 "Expected tagged object, found " + asn1Encodable.getClass().getName());
366     }
367 
parseAsn1Encodable(ASN1SequenceParser parser)368     private static ASN1Encodable parseAsn1Encodable(ASN1SequenceParser parser)
369             throws CertificateParsingException {
370         try {
371             return parser.readObject();
372         } catch (IOException e) {
373             throw new CertificateParsingException("Failed to parse ASN1 sequence", e);
374         }
375     }
376 
getPurposes()377     public Set<Integer> getPurposes() {
378         return purposes;
379     }
380 
getAlgorithm()381     public Integer getAlgorithm() {
382         return algorithm;
383     }
384 
getKeySize()385     public Integer getKeySize() {
386         return keySize;
387     }
388 
getDigests()389     public Set<Integer> getDigests() {
390         return digests;
391     }
392 
getPaddingModes()393     public Set<Integer> getPaddingModes() {
394         return paddingModes;
395     }
396 
getPaddingModesAsStrings()397     public Set<String> getPaddingModesAsStrings() throws CertificateParsingException {
398         if (paddingModes == null) {
399             return ImmutableSet.of();
400         }
401 
402         ImmutableSet.Builder<String> builder = ImmutableSet.builder();
403         for (int paddingMode : paddingModes) {
404             switch (paddingMode) {
405                 case KM_PAD_NONE:
406                     builder.add(KeyProperties.ENCRYPTION_PADDING_NONE);
407                     break;
408                 case KM_PAD_RSA_OAEP:
409                     builder.add(KeyProperties.ENCRYPTION_PADDING_RSA_OAEP);
410                     break;
411                 case KM_PAD_RSA_PKCS1_1_5_ENCRYPT:
412                     builder.add(KeyProperties.ENCRYPTION_PADDING_RSA_PKCS1);
413                     break;
414                 case KM_PAD_RSA_PKCS1_1_5_SIGN:
415                     builder.add(KeyProperties.SIGNATURE_PADDING_RSA_PKCS1);
416                     break;
417                 case KM_PAD_RSA_PSS:
418                     builder.add(KeyProperties.SIGNATURE_PADDING_RSA_PSS);
419                     break;
420                 default:
421                     throw new CertificateParsingException("Invalid padding mode " + paddingMode);
422             }
423         }
424         return builder.build();
425     }
426 
getEcCurve()427     public Integer getEcCurve() {
428         return ecCurve;
429     }
430 
ecCurveAsString()431     public String ecCurveAsString() {
432         if (ecCurve == null)
433             return "NULL";
434 
435         switch (ecCurve) {
436             case KM_EC_CURVE_P224:
437                 return "secp224r1";
438             case KM_EC_CURVE_P256:
439                 return "secp256r1";
440             case KM_EC_CURVE_P384:
441                 return "secp384r1";
442             case KM_EC_CURVE_P521:
443                 return "secp521r1";
444             default:
445                 return "unknown";
446         }
447     }
448 
getRsaPublicExponent()449     public Long getRsaPublicExponent() {
450         return rsaPublicExponent;
451     }
452 
getActiveDateTime()453     public Date getActiveDateTime() {
454         return activeDateTime;
455     }
456 
getOriginationExpireDateTime()457     public Date getOriginationExpireDateTime() {
458         return originationExpireDateTime;
459     }
460 
getUsageExpireDateTime()461     public Date getUsageExpireDateTime() {
462         return usageExpireDateTime;
463     }
464 
isNoAuthRequired()465     public boolean isNoAuthRequired() {
466         return noAuthRequired;
467     }
468 
getUserAuthType()469     public Integer getUserAuthType() {
470         return userAuthType;
471     }
472 
getAuthTimeout()473     public Integer getAuthTimeout() {
474         return authTimeout;
475     }
476 
isAllowWhileOnBody()477     public boolean isAllowWhileOnBody() {
478         return allowWhileOnBody;
479     }
480 
isAllApplications()481     public boolean isAllApplications() {
482         return allApplications;
483     }
484 
getApplicationId()485     public byte[] getApplicationId() {
486         return applicationId;
487     }
488 
getCreationDateTime()489     public Date getCreationDateTime() {
490         return creationDateTime;
491     }
492 
getOrigin()493     public Integer getOrigin() {
494         return origin;
495     }
496 
isRollbackResistant()497     public boolean isRollbackResistant() {
498         return rollbackResistant;
499     }
500 
getRootOfTrust()501     public RootOfTrust getRootOfTrust() {
502         return rootOfTrust;
503     }
504 
getOsVersion()505     public Integer getOsVersion() {
506         return osVersion;
507     }
508 
getOsPatchLevel()509     public Integer getOsPatchLevel() {
510         return osPatchLevel;
511     }
512 
getVendorPatchLevel()513     public Integer getVendorPatchLevel() { return vendorPatchLevel; }
514 
getBootPatchLevel()515     public Integer getBootPatchLevel() { return bootPatchLevel; }
516 
getAttestationApplicationId()517     public AttestationApplicationId getAttestationApplicationId() {
518         return attestationApplicationId;
519     }
520 
getModuleHash()521     public byte[] getModuleHash() {
522         return moduleHash.clone();
523     }
524 
525     @Override
toString()526     public String toString() {
527         StringBuilder s = new StringBuilder();
528 
529         if (algorithm != null) {
530             s.append("\nAlgorithm: ").append(algorithmToString(algorithm));
531         }
532 
533         if (keySize != null) {
534             s.append("\nKeySize: ").append(keySize);
535         }
536 
537         if (purposes != null && !purposes.isEmpty()) {
538             s.append("\nPurposes: ").append(purposesToString(purposes));
539         }
540 
541         if (digests != null && !digests.isEmpty()) {
542             s.append("\nDigests: ").append(digestsToString(digests));
543         }
544 
545         if (paddingModes != null && !paddingModes.isEmpty()) {
546             s.append("\nPadding modes: ").append(paddingModesToString(paddingModes));
547         }
548 
549         if (ecCurve != null) {
550             s.append("\nEC Curve: ").append(ecCurveAsString());
551         }
552 
553         String label = "\nRSA exponent: ";
554         if (rsaPublicExponent != null) {
555             s.append(label).append(rsaPublicExponent);
556         }
557 
558         if (activeDateTime != null) {
559             s.append("\nActive: ").append(formatDate(activeDateTime));
560         }
561 
562         if (originationExpireDateTime != null) {
563             s.append("\nOrigination expire: ").append(formatDate(originationExpireDateTime));
564         }
565 
566         if (usageExpireDateTime != null) {
567             s.append("\nUsage expire: ").append(formatDate(usageExpireDateTime));
568         }
569 
570         if (!noAuthRequired && userAuthType != null) {
571             s.append("\nAuth types: ").append(userAuthTypeToString(userAuthType));
572             if (authTimeout != null) {
573                 s.append("\nAuth timeout: ").append(authTimeout);
574             }
575         }
576 
577         if (applicationId != null) {
578             s.append("\nApplication ID: ").append(new String(applicationId));
579         }
580 
581         if (creationDateTime != null) {
582             s.append("\nCreated: ").append(formatDate(creationDateTime));
583         }
584 
585         if (origin != null) {
586             s.append("\nOrigin: ").append(originToString(origin));
587         }
588 
589         if (rollbackResistant) {
590             s.append("\nRollback resistant: true");
591         }
592 
593         if (rootOfTrust != null) {
594             s.append("\nRoot of Trust:\n");
595             s.append(rootOfTrust);
596         }
597 
598         if (osVersion != null) {
599             s.append("\nOS Version: ").append(osVersion);
600         }
601 
602         if (osPatchLevel != null) {
603             s.append("\nOS Patchlevel: ").append(osPatchLevel);
604         }
605 
606         if (vendorPatchLevel != null) {
607             s.append("\nVendor Patchlevel: ").append(vendorPatchLevel);
608         }
609 
610         if (bootPatchLevel != null) {
611             s.append("\nBoot Patchlevel: ").append(bootPatchLevel);
612         }
613 
614         if (attestationApplicationId != null) {
615             s.append("\nAttestation Application ID:").append(attestationApplicationId.toString());
616         }
617 
618         if (moduleHash != null) {
619             s.append("\nModule Hash: ").append(Hex.toHexString(moduleHash));
620         }
621 
622         return s.toString();
623     }
624 }
625