-
Notifications
You must be signed in to change notification settings - Fork 106
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add Samsung Knox TEE integrity status #15
base: master
Are you sure you want to change the base?
Conversation
The main problem here is that keystore2 will prevent generating a SAK key if the app doesn't have one of those runtime permissions declared and granted: <!-- android.Manifest.permission.KNOX_CCM_KEYSTORE -->
<permission
android:label="@string/permlab_mdmCcm"
android:description="@string/permdesc_mdmCcm"
android:name="com.samsung.android.knox.permission.KNOX_CCM_KEYSTORE"
android:permissionGroup="com.sec.enterprise.permission-group.mdm"
android:protectionLevel="signature" />
<!-- android.Manifest.permission.KNOX_TIMA_KEYSTORE -->
<permission
android:label="@string/permlab_mdmKeystore"
android:description="@string/permdesc_mdmKeystore"
android:name="com.samsung.android.knox.permission.KNOX_TIMA_KEYSTORE"
android:permissionGroup="com.sec.enterprise.permission-group.mdm"
android:protectionLevel="signature" />
<!-- android.Manifest.permission.KNOX_TIMA_KEYSTORE_PER_APP -->
<permission
android:label="@string/permlab_mdmKeystorePerApp"
android:description="@string/permdesc_mdmKeystorePerApp"
android:name="com.samsung.android.knox.permission.KNOX_TIMA_KEYSTORE_PER_APP"
android:permissionGroup="com.sec.enterprise.permission-group.mdm"
android:protectionLevel="signature" />
<!-- android.Manifest.permission.SAMSUNG_KEYSTORE_PERMISSION -->
<permission
android:name="com.samsung.android.security.permission.SAMSUNG_KEYSTORE_PERMISSION"
android:protectionLevel="signatureOrSystem" /> ...and I've currently haven't found a way to grant |
Thanks for your PR! I'm a bit surprised that Samsung has another system (even a different root of trust). Maybe use adb shell, which has more privileges than app and doesn't need unlock bootloader. This is in my plan, but for now I want to merge the parsing part first, hope you don't mind. Can you upload the saved pkipath file for me to test? |
I did not find the public key published by Samsung on the Internet, can you provide the source? |
Sure! Here is it a52sxqeea-KeyAttestation.zip
SKeymaster doesn't differs much from AOSP keymaster, Samsung enables Knox-specific code when asked via passing additional KeyParameters to keymaster as I discovered here: private KeyParameter[] constructAttestationArguments(AttestParameterSpec spec) throws IllegalArgumentException, NullPointerException {
if (spec.getChallenge() == null) {
throw new IllegalArgumentException("The challenge cannot be null");
}
ArrayList<KeyParameter> args = new ArrayList<>();
args.add(makeBytes(KeymasterDefs.KM_TAG_ATTESTATION_CHALLENGE, spec.getChallenge()));
if (spec.isDeviceAttestation()) {
args.add(makeBytes(KeymasterDefs.KM_TAG_SAMSUNG_ATTESTATION_ROOT, SAMSUNG_ATTESTESTATION_DEVICE_IDS_ROOT.getBytes()));
} else {
args.add(makeBytes(KeymasterDefs.KM_TAG_SAMSUNG_ATTESTATION_ROOT, SAMSUNG_ATTESTESTATION_ROOT.getBytes()));
}
X500Principal certificateSubject = spec.getCertificateSubject();
if (certificateSubject != null && !TextUtils.isEmpty(certificateSubject.getName("RFC1779"))) {
args.add(makeBytes(KeymasterDefs.KM_TAG_SAMSUNG_CERTIFICATE_SUBJECT, certificateSubject.getName("RFC1779").getBytes()));
}
if (spec.isVerifiableIntegrity()) {
args.add(makeBool(KeymasterDefs.KM_TAG_SAMSUNG_ATTEST_INTEGRITY));
Application application = ActivityThread.currentApplication();
if (application == null) {
Log.w(TAG, "can not found application");
} else {
String packageName = spec.getPackageName();
if (TextUtils.isEmpty(packageName)) {
packageName = application.getPackageName();
}
byte[] bytesAuthPkg = getBytesAuthenticatePackage(packageName, application);
if (bytesAuthPkg == null) {
Log.w(TAG, "Auth package byte is null");
} else {
args.add(makeBytes(KeymasterDefs.KM_TAG_SAMSUNG_AUTHENTICATE_PACKAGE, bytesAuthPkg));
}
}
}
if (spec.isDevicePropertiesAttestationIncluded()) {
args.add(makeBytes(KeymasterDefs.KM_TAG_ATTESTATION_ID_BRAND, Build.BRAND.getBytes(StandardCharsets.UTF_8)));
args.add(makeBytes(KeymasterDefs.KM_TAG_ATTESTATION_ID_DEVICE, Build.DEVICE.getBytes(StandardCharsets.UTF_8)));
args.add(makeBytes(KeymasterDefs.KM_TAG_ATTESTATION_ID_PRODUCT, Build.PRODUCT.getBytes(StandardCharsets.UTF_8)));
args.add(makeBytes(KeymasterDefs.KM_TAG_ATTESTATION_ID_MANUFACTURER, Build.MANUFACTURER.getBytes(StandardCharsets.UTF_8)));
args.add(makeBytes(KeymasterDefs.KM_TAG_ATTESTATION_ID_MODEL, Build.MODEL.getBytes(StandardCharsets.UTF_8)));
}
return (KeyParameter[]) args.toArray(new KeyParameter[args.size()]);
} |
I was able to get it thanks to the debug logs in your app: Line 71 in b73bb6d
I've added it to hide the "not trusted" header in the app. |
Due to the significance of root of trust, the app can only accept official public data, such as Google. |
Afaict SAK is only used by Samsung Knox powered apps (except for Samsung Pay/Wallet and Pass), mostly to verify whether or not the device is tampered (the key generation fails when ICD status is abnormal unless isVerifyIntegrity is true), probably the reason why Samsung didn't publish their public key anywhere.
Yes (a52sxqeea-KeyAttestation.zip). |
I guess it might be in the internal Samsung Knox SDK doc, if you can confirm the public key is the same for all Samsung devices, I will add it. |
Tested myself on two devices (Galaxy A52s 5G with Android 13, Galaxy A71 with Android 11) and the public key is the same. |
The data cannot be fully parsed. |
Are you talking about the 6th entry? I didn't include that on purpose as it isn't relevant private static final int AUTH_RESULT = 5;
private AuthResult mAuthResult = null;
public IntegrityStatus(ASN1Primitive asn1Encodable) {
ASN1Sequence sequence = (ASN1Sequence) asn1Encodable;
Enumeration<?> seqEnum = sequence.getObjects();
while (seqEnum.hasMoreElements()) {
ASN1TaggedObject derObj = (ASN1TaggedObject) seqEnum.nextElement();
switch (derObj.getTagNo()) {
// ...
case AUTH_RESULT:
mAuthResult = new AuthResult(derObj.getObject());
break;
}
}
}
@Override
public String toString() {
// ...
.append("Caller auth (with PROCA) status:").append('\n');
sb.append(mAuthResult == null ? "Not performed" : mAuthResult.toString());
return sb.toString();
} This is how the AuthResult class looks like: package com.android.server.knox.dar;
import android.util.Log;
import org.bouncycastle.asn1.ASN1Enumerated;
import org.bouncycastle.asn1.ASN1Primitive;
import org.bouncycastle.asn1.ASN1Sequence;
import org.bouncycastle.asn1.ASN1TaggedObject;
import java.security.cert.CertificateParsingException;
import java.util.Enumeration;
public class AuthResult {
private static final String TAG = "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 mCallerAuthResult = -1;
private byte[] mCallingPackage = new byte[]{0};
private byte[] mCallingPackageSigs = new byte[]{0};
private int mCallingPackageAuthResult = -1;
public AuthResult(ASN1Primitive asn1Encodable) throws CertificateParsingException {
if (!(asn1Encodable instanceof ASN1Sequence)) {
throw new CertificateParsingException("Expected sequence for root of trust, found " + asn1Encodable.getClass().getName());
}
ASN1Sequence sequence = (ASN1Sequence) asn1Encodable;
Enumeration seqEnum = sequence.getObjects();
while (seqEnum.hasMoreElements()) {
ASN1TaggedObject derObj = (ASN1TaggedObject) seqEnum.nextElement();
switch (derObj.getTagNo()) {
case CALLER_AUTH_RESULT:
mCallerAuthResult = ((ASN1Enumerated) derObj.getObject()).getValue().intValue();
break;
case CALLING_PACKAGE:
mCallingPackage = Asn1Utils.getByteArrayFromAsn1(derObj);
break;
case CALLING_PACKAGE_SIGS:
mCallingPackageSigs = Asn1Utils.getByteArrayFromAsn1(derObj);
break;
case CALLING_PACKAGE_AUTH_RESULT:
mCallingPackageAuthResult = ((ASN1Enumerated) derObj.getObject()).getValue().intValue();
break;
default:
Log.e(TAG, "invalid tag no : " + derObj.getTagNo());
break;
}
}
}
public int getCallerAuthResult() {
return mCallerAuthResult;
}
public byte[] getCallingPackage() {
return mCallingPackage;
}
public byte[] getCallingPackageSigs() {
return mCallingPackageSigs;
}
public int getCallingPackageAuthResult() {
return mCallingPackageAuthResult;
}
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(mCallerAuthResult, false))
.append("\n Calling Package : ")
.append(new String(mCallingPackage))
.append("\n Calling Package Signatures : ")
.append(new String(mCallingPackageSigs))
.append("\n Calling Package Auth Result : ")
.append(statusToString(mCallingPackageAuthResult, true));
return sb.toString();
}
} |
what about this? |
typedef struct KnoxTEEProperties {
ASN1_PRINTABLESTRING *challenge;
ACCESSOR *creator;
ACCESSOR_SET *administrators;
ACCESSOR_SET *accessors;
ASN1_PRINTABLESTRING *id_attest;
INTEGRITY_STATUS *integrity;
ASN1_OCTET_STRING *attestation_record_hash;
} KNOX_TEE_PROPERTIES;
ASN1_SEQUENCE(KNOX_TEE_PROPERTIES) = {
ASN1_EXP_OPT(KNOX_TEE_PROPERTIES, challenge, ASN1_PRINTABLESTRING, 0),
ASN1_EXP_OPT(KNOX_TEE_PROPERTIES, creator, ACCESSOR, 1),
ASN1_EXP_SET_OF_OPT(KNOX_TEE_PROPERTIES, administrators, ACCESSOR, 2),
ASN1_EXP_SET_OF_OPT(KNOX_TEE_PROPERTIES, accessors, ACCESSOR, 3),
ASN1_EXP_OPT(KNOX_TEE_PROPERTIES, id_attest, ASN1_PRINTABLESTRING, 4),
ASN1_EXP_OPT(KNOX_TEE_PROPERTIES, integrity, INTEGRITY_STATUS, 5),
ASN1_EXP_OPT(KNOX_TEE_PROPERTIES, attestation_record_hash, ASN1_OCTET_STRING, 6),
} ASN1_SEQUENCE_END(KNOX_TEE_PROPERTIES) So |
Updated the PR to adapt to the latest source changes, also did some minor code cleanup. However, it looks like the app generated attest key feature isn't working correctly (signature error:error decoding signature bytes.) |
You are in app attest key mode, it looks like Samsung does not support it, you need to remove this feature for Samsung service. |
@blackmesa123 Samsung uses Google private key to sign the leaf certificate? Or it uses Samsung custom one? EDIT: They use their private key :( |
|
What happens if fill it with a random byte array? This is a non-printable string.
Have they been deprecated and removed? I only find them in KnoxKeyInfo |
|
a92bc7a
to
26db9eb
Compare
Seems like it. The keymaster TA source code I checked, which is part of the Samsung 2022 leak, is based off a very old version (4.2.19, used in Android 11 Samsung OS), each of these values are respectly set via the |
About the recent pushes in master branch:
All the rest seems okay |
I'm a little hesitant to add them, they were deprecated probably because of private key leak. Perhaps they should be considered revoked instead of legacy. Also, I'd like to add them after testing that the certificate chain samples can be parsed properly. |
I don't think Samsung certificates ever got leaked, afaict SAK v2 cert has started to appear on devices shipping with Android 10 onwards, either because they implemented KeyMaster 4 or ICCC v4. SAK v1 is still a thing on legacy devices such as Galaxy S10 and Samsung apps/services that use SAK (Samsung Health, Knox Matrix) still accepts those certs https://github.com/corsicanu/random_dumps/releases?q=sak%2C&expanded=true |
What is the difference between sakmv1 and sakv1? Do they still sign certs without tags 1-4 (creator,administrators,accessors,id_attest) like v2? |
Certificate probably doesn't matters, what matters is the Knox SDK the device is running on (Android version). I couldn't find any mention to these tags from Android 12 onwards and I think this is the reason: https://docs.samsungknox.com/dev/knox-sdk/features/mdm-providers/keystores/tima-ccm-keystores/deprecation-of-tima-ccm-keystore-support/ These tags are controlled via the |
SAKm_V1 cert seems to be used on Samsung JDM devices (eg. SM-T505) |
Signed-off-by: BlackMesa123 <[email protected]>
Signed-off-by: BlackMesa123 <[email protected]>
Signed-off-by: BlackMesa123 <[email protected]>
Signed-off-by: BlackMesa123 <[email protected]>
Signed-off-by: BlackMesa123 <[email protected]>
Signed-off-by: BlackMesa123 <[email protected]>
https://gitgud.io/Van-Firmware-Dumps/samsung/a16xm/-/blob/a16xmnsxx-user-14-UP1A.231005.007-A166PXXU1AXIC-release-keys/vendor/etc/permissions/samsung.software.sakm_uid.xml I don't know why |
First one is probably a leftover since the feature name is "android.software.device_id_attestation" just like on AOSP. I never saw the second one and I can't seem to find it in Samsung in-house devices, so that code might be exclusive to Samsung JDM devices/firmwares (https://gitgud.io/Van-Firmware-Dumps/samsung/a16xm/-/blob/a16xmnsxx-user-14-UP1A.231005.007-A166PXXU1AXIC-release-keys/system/system/etc/floating_feature.xml?ref_type=heads#L66): <SEC_FLOATING_FEATURE_COMMON_CONFIG_DEVICE_MANUFACTURING_TYPE>jdm</SEC_FLOATING_FEATURE_COMMON_CONFIG_DEVICE_MANUFACTURING_TYPE> public static final int KM_TAG_SAMSUNG_UID_REQUIRED = 0x700009c4; // KM_BOOL | 2500; if (spec.isSAKUidRequired()) {
Log.w(TAG, "constructAttestationArguments : set SAK UID required");
args.add(makeBool(KeymasterDefs.KM_TAG_SAMSUNG_UID_REQUIRED));
} SAK UID can be seen in the intermediate cert (eg. "SAK_V2:20211230082636:520:v0MGMlvJ_ExyvfGKYqGa4YwNrr0C0jUesO3BCqhcpVA=:wUxAmaFZhPUS8SR62oJns3jToAme0Gwj1jEH4sDcSAE="), perhaps this works differently on JDM devices with SAKm cert? Gotta inspect the KeyMint TA. |
SAK UID check takes place when requesting key attestation with |
This PR adds the ability to retrieve and show Samsung's specific device status info, this is done by using their own keystore API's to generate a "Knox protected" key. Additional code refactoring are welcome.