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