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