-
Notifications
You must be signed in to change notification settings - Fork 112
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
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 |
|
last-modified: Fri, 15 Dec 2023 14:48:00 GMT
last-modified: Mon, 22 Jan 2024 17:18:00 GMT
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 |
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) |
last-modified: Tue, 20 Feb 2024 15:18:00 GMT
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 |
The app only supports Knox attestation on Android 10+. I'm not sure if I should add SAKv1 certificates. I haven't seen any other root public key signed certificates. |
|
All merged, please check if there is anything missing |
Tested upstream commit (b5d4675) on both my A52s and A54 (Keymaster vs. KeyMint 2) and everything seems fine Unfortunately just like before Shizuku alone isn't enough to grant the SAMSUNG_KEYSTORE_PERMISSION permission (#15 (comment)) so I still had to push the apk to priv-app to allow using the Samsung attestation SDK |
You can start shizuku with root instead of adb shell. From the screenshot it looks like you have already done this, try moving the app out of priv-app and it should still work. |
I confirm using Shizuku with root permissions also works without the requirement of having to push the apk in priv-app, looks good for a final release to me! I'd keep the |
While I was searching for this prop to make sure its selinux context allowed user app to read it, I found some posts about remove sak. So I ignore this check, and if the device really doesn't support it, then the exception will be caught, just like gak. KeyAttestation/app/src/main/java/io/github/vvb2060/keyattestation/keystore/AndroidKeyStore.java Lines 263 to 279 in b5d4675
|
This is erroneously done to "bypass" security checks in some Knox apps which can work without Samsung attestation (eg. Samsung Health), but shouldn't be done as it breaks some core system features which instead heavily rely on SAK to work (eg. FMM), so I wouldn't account the user stupidity to touch stuff they shouldn't. KnoxPatch/OS patches should be used instead to bypass Samsung Knox security checks in such apps |
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.