Skip to content

Commit

Permalink
app: display Knox TEE integrity status info
Browse files Browse the repository at this point in the history
Signed-off-by: BlackMesa123 <[email protected]>
  • Loading branch information
salvogiangri committed Nov 16, 2023
1 parent 6f18c6c commit 9de9049
Show file tree
Hide file tree
Showing 8 changed files with 366 additions and 4 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
import org.bouncycastle.asn1.ASN1Sequence;
import org.bouncycastle.asn1.ASN1Set;
import org.bouncycastle.asn1.DEROctetString;
import org.bouncycastle.asn1.DERPrintableString;

import java.io.IOException;
import java.io.UnsupportedEncodingException;
Expand Down Expand Up @@ -63,11 +64,16 @@ public static Long getLongFromAsn1(ASN1Encodable asn1Value) throws CertificatePa

public static byte[] getByteArrayFromAsn1(ASN1Encodable asn1Encodable)
throws CertificateParsingException {
if (asn1Encodable == null || !(asn1Encodable instanceof DEROctetString)) {
if (asn1Encodable == null) {
throw new CertificateParsingException("Expected DEROctetString");
}
ASN1OctetString derOctectString = (ASN1OctetString) asn1Encodable;
return derOctectString.getOctets();
if (asn1Encodable instanceof DEROctetString) {
return ((ASN1OctetString) asn1Encodable).getOctets();
}
if (asn1Encodable instanceof DERPrintableString) {
return ((DERPrintableString) asn1Encodable).getOctets();
}
throw new CertificateParsingException("Expected DEROctetString");
}

public static ASN1Encodable getAsn1EncodableFromBytes(byte[] bytes)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
* contents.
*/
public abstract class Attestation {
static final String KNOX_EXTENSION_OID = "1.3.6.1.4.1.236.11.3.23.7";
static final String EAT_OID = "1.3.6.1.4.1.11129.2.1.25";
static final String ASN1_OID = "1.3.6.1.4.1.11129.2.1.17";
static final String KEY_USAGE_OID = "2.5.29.15"; // Standard key usage extension.
Expand Down Expand Up @@ -64,10 +65,17 @@ public abstract class Attestation {
*/

public static Attestation loadFromCertificate(X509Certificate x509Cert) throws CertificateParsingException {
if (x509Cert.getExtensionValue(EAT_OID) == null
if (x509Cert.getExtensionValue(KNOX_EXTENSION_OID) == null
&& x509Cert.getExtensionValue(EAT_OID) == null
&& x509Cert.getExtensionValue(ASN1_OID) == null) {
throw new CertificateParsingException("No attestation extensions found");
}
if (x509Cert.getExtensionValue(KNOX_EXTENSION_OID) != null) {
if (x509Cert.getExtensionValue(EAT_OID) != null) {
throw new CertificateParsingException("Multiple attestation extensions found");
}
return new KnoxAttestation(x509Cert);
}
if (x509Cert.getExtensionValue(EAT_OID) != null) {
if (x509Cert.getExtensionValue(ASN1_OID) != null) {
throw new CertificateParsingException("Multiple attestation extensions found");
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
package io.github.vvb2060.keyattestation.attestation;

import com.google.common.io.BaseEncoding;

import org.bouncycastle.asn1.ASN1Encodable;
import org.bouncycastle.asn1.ASN1Primitive;
import org.bouncycastle.asn1.ASN1Sequence;
import org.bouncycastle.asn1.ASN1SequenceParser;
import org.bouncycastle.asn1.ASN1TaggedObject;

import java.io.IOException;
import java.security.cert.CertificateParsingException;

public class AuthResult {
private static final int CALLER_AUTH_RESULT = 0;
private static final int CALLING_PACKAGE = 1;
private static final int CALLING_PACKAGE_SIGS = 2;
private static final int CALLING_PACKAGE_AUTH_RESULT = 3;

public static final int STATUS_NORMAL = 0;
public static final int STATUS_ABNORMAL = 1;
public static final int STATUS_NOT_SUPPORT = 2;

private int callerAuthResult;
private byte[] callingPackage;
private byte[] callingPackageSigs;
private int callingPackageAuthResult;

public AuthResult(ASN1Encodable asn1Encodable) throws CertificateParsingException {
if (!(asn1Encodable instanceof ASN1Sequence sequence)) {
throw new CertificateParsingException("Expected sequence for caller auth, found "
+ asn1Encodable.getClass().getName());
}

ASN1SequenceParser parser = sequence.parser();
ASN1TaggedObject entry = parseAsn1TaggedObject(parser);

for (; entry != null; entry = parseAsn1TaggedObject(parser)) {
int tag = entry.getTagNo();
ASN1Primitive value = entry.getBaseObject().toASN1Primitive();

switch (tag) {
case CALLER_AUTH_RESULT:
callerAuthResult = Asn1Utils.getIntegerFromAsn1(value);
break;
case CALLING_PACKAGE:
callingPackage = Asn1Utils.getByteArrayFromAsn1(value);
break;
case CALLING_PACKAGE_SIGS:
callingPackageSigs = Asn1Utils.getByteArrayFromAsn1(value);
break;
case CALLING_PACKAGE_AUTH_RESULT:
callingPackageAuthResult = Asn1Utils.getIntegerFromAsn1(value);
break;
}
}
}

private static ASN1TaggedObject parseAsn1TaggedObject(ASN1SequenceParser parser)
throws CertificateParsingException {
ASN1Encodable asn1Encodable = parseAsn1Encodable(parser);
if (asn1Encodable == null || asn1Encodable instanceof ASN1TaggedObject) {
return (ASN1TaggedObject) asn1Encodable;
}
throw new CertificateParsingException(
"Expected tagged object, found " + asn1Encodable.getClass().getName());
}

private static ASN1Encodable parseAsn1Encodable(ASN1SequenceParser parser)
throws CertificateParsingException {
try {
return parser.readObject();
} catch (IOException e) {
throw new CertificateParsingException("Failed to parse ASN1 sequence", e);
}
}

public String statusToString(int status, boolean isCallingPackageAuthResult) {
switch (status) {
case STATUS_NORMAL:
return "Normal";
case STATUS_ABNORMAL:
return "Abnormal";
case STATUS_NOT_SUPPORT:
return "Not support";
default:
if (isCallingPackageAuthResult) {
return "Not support";
}
return Integer.toHexString(status);
}
}

@Override
public String toString() {
StringBuilder sb = new StringBuilder("Caller auth result: ")
.append(statusToString(callerAuthResult, false)).append('\n')
.append("Calling package: ")
.append(new String(callingPackage)).append('\n')
.append("Calling package signatures: ")
.append(BaseEncoding.base64().encode(callingPackageSigs)).append(" (base64)").append('\n')
.append("Calling package auth result: ")
.append(statusToString(callingPackageAuthResult, true));
return sb.toString();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -226,6 +226,7 @@ public class AuthorizationList {
private Boolean rollbackResistant;
private Boolean rollbackResistance;
private RootOfTrust rootOfTrust;
private IntegrityStatus integrityStatus;
private Integer osVersion;
private Integer osPatchLevel;
private Integer vendorPatchLevel;
Expand Down Expand Up @@ -767,6 +768,14 @@ public RootOfTrust getRootOfTrust() {
return rootOfTrust;
}

public IntegrityStatus getIntegrityStatus() {
return integrityStatus;
}

void setIntegrityStatus(IntegrityStatus is) {
integrityStatus = is;
}

public Integer getOsVersion() {
return osVersion;
}
Expand Down Expand Up @@ -960,6 +969,15 @@ public String toString() {
s.append(rootOfTrust);
}

if (integrityStatus != null) {
s.append("\nIntegrity Status:\n");
s.append(integrityStatus);
if (integrityStatus.getAuthResult() != null) {
s.append("\nCaller Auth Status:\n");
s.append(integrityStatus.getAuthResult());
}
}

if (osVersion != null) {
s.append("\nOS Version: ").append(osVersion);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
package io.github.vvb2060.keyattestation.attestation;

import org.bouncycastle.asn1.ASN1Encodable;
import org.bouncycastle.asn1.ASN1Primitive;
import org.bouncycastle.asn1.ASN1Sequence;
import org.bouncycastle.asn1.ASN1SequenceParser;
import org.bouncycastle.asn1.ASN1TaggedObject;

import java.io.IOException;
import java.security.cert.CertificateParsingException;

public class IntegrityStatus {
private static final int TRUST_BOOT = 0;
private static final int WARRANTY = 1;
private static final int ICD = 2;
private static final int KERNEL_STATUS = 3;
private static final int SYSTEM_STATUS = 4;
private static final int AUTH_RESULT = 5;

public static final int STATUS_NORMAL = 0;
public static final int STATUS_ABNORMAL = 1;
public static final int STATUS_NOT_SUPPORT = 2;

private int trustBoot;
private int warranty;
private int icd;
private int kernelStatus;
private int systemStatus;
private AuthResult authResult;

public IntegrityStatus(ASN1Encodable asn1Encodable) throws CertificateParsingException {
if (!(asn1Encodable instanceof ASN1Sequence sequence)) {
throw new CertificateParsingException("Expected sequence for integrity status, found "
+ asn1Encodable.getClass().getName());
}

ASN1SequenceParser parser = sequence.parser();
ASN1TaggedObject entry = parseAsn1TaggedObject(parser);

for (; entry != null; entry = parseAsn1TaggedObject(parser)) {
int tag = entry.getTagNo();
ASN1Primitive value = entry.getBaseObject().toASN1Primitive();

switch (tag) {
case TRUST_BOOT:
trustBoot = Asn1Utils.getIntegerFromAsn1(value);
break;
case WARRANTY:
warranty = Asn1Utils.getIntegerFromAsn1(value);
break;
case ICD:
icd = Asn1Utils.getIntegerFromAsn1(value);
break;
case KERNEL_STATUS:
kernelStatus = Asn1Utils.getIntegerFromAsn1(value);
break;
case SYSTEM_STATUS:
systemStatus = Asn1Utils.getIntegerFromAsn1(value);
break;
case AUTH_RESULT:
authResult = new AuthResult(value);
break;
}
}
}

private static ASN1TaggedObject parseAsn1TaggedObject(ASN1SequenceParser parser)
throws CertificateParsingException {
ASN1Encodable asn1Encodable = parseAsn1Encodable(parser);
if (asn1Encodable == null || asn1Encodable instanceof ASN1TaggedObject) {
return (ASN1TaggedObject) asn1Encodable;
}
throw new CertificateParsingException(
"Expected tagged object, found " + asn1Encodable.getClass().getName());
}

private static ASN1Encodable parseAsn1Encodable(ASN1SequenceParser parser)
throws CertificateParsingException {
try {
return parser.readObject();
} catch (IOException e) {
throw new CertificateParsingException("Failed to parse ASN1 sequence", e);
}
}

public AuthResult getAuthResult() {
return authResult;
}

public String statusToString(int status) {
switch (status) {
case STATUS_NORMAL:
return "Normal";
case STATUS_ABNORMAL:
return "Abnormal";
case STATUS_NOT_SUPPORT:
return "Not support";
default:
return Integer.toHexString(status);
}
}

@Override
public String toString() {
StringBuilder sb = new StringBuilder("Trustboot: ")
.append(statusToString(trustBoot)).append('\n')
.append("Warranty bit: ")
.append(statusToString(warranty)).append('\n')
.append("ICD: ")
.append(statusToString(icd)).append('\n')
.append("Kernel status: ")
.append(statusToString(kernelStatus)).append('\n')
.append("System status: ")
.append(statusToString(systemStatus));
return sb.toString();
}
}
Loading

0 comments on commit 9de9049

Please sign in to comment.