mirror of
https://github.com/KDE/kdeconnect-android
synced 2025-08-22 01:51:47 +00:00
Add a verification key that's displayed when pairing
The key is a sha256 of both devices' certificates. Both should generate the same key, so hey user can check they are pairing against the right device. Thanks Matthias Gerstner <mgerstner@suse.de> for reporting this.
This commit is contained in:
parent
dd210259b9
commit
d09ad45e11
10
res/drawable/ic_key.xml
Normal file
10
res/drawable/ic_key.xml
Normal file
@ -0,0 +1,10 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24"
|
||||
android:tint="?attr/colorControlNormal">
|
||||
<path
|
||||
android:fillColor="@android:color/white"
|
||||
android:pathData="M12.65,10C11.83,7.67 9.61,6 7,6c-3.31,0 -6,2.69 -6,6s2.69,6 6,6c2.61,0 4.83,-1.67 5.65,-4H17v4h4v-4h2v-4H12.65zM7,14c-1.1,0 -2,-0.9 -2,-2s0.9,-2 2,-2 2,0.9 2,2 -0.9,2 -2,2z"/>
|
||||
</vector>
|
@ -30,6 +30,18 @@
|
||||
android:text="@string/device_not_paired"
|
||||
android:textAppearance="?android:attr/textAppearanceMedium"/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/pair_verification"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:drawablePadding="5dp"
|
||||
android:drawableLeft="@drawable/ic_key"
|
||||
android:drawableStart="@drawable/ic_key"
|
||||
android:layout_marginBottom="8dip"
|
||||
android:visibility="gone"
|
||||
android:text=""
|
||||
android:textAppearance="?android:attr/textAppearanceMedium"/>
|
||||
|
||||
<Button
|
||||
android:id="@+id/pair_button"
|
||||
android:layout_width="match_parent"
|
||||
|
@ -126,9 +126,10 @@
|
||||
<string name="encryption_info_title">Encryption Info</string>
|
||||
<string name="encryption_info_msg_no_ssl">The other device doesn\'t use a recent version of KDE Connect, using the legacy encryption method.</string>
|
||||
<string name="my_device_fingerprint">SHA1 fingerprint of your device certificate is:</string>
|
||||
<string name="remote_device_fingerprint">SHA1 fingerprint of remote device certificate is:</string>
|
||||
<string name="remote_device_fingerprint">SHA256 fingerprint of remote device certificate is:</string>
|
||||
<string name="pair_requested">Pair requested</string>
|
||||
<string name="pairing_request_from">Pairing request from %1s</string>
|
||||
<string name="pairing_verification_code" translatable="false">🔑%1s...</string>
|
||||
<plurals name="incoming_file_title">Receiving file from %1s>
|
||||
<item quantity="one">Receiving %1$d file from %2$s</item>
|
||||
<item quantity="other">Receiving %1$d files from %2$s</item>
|
||||
|
@ -391,10 +391,11 @@ public class Device implements BaseLink.PacketReceiver {
|
||||
|
||||
final NotificationManager notificationManager = ContextCompat.getSystemService(getContext(), NotificationManager.class);
|
||||
|
||||
String verificationKeyShort = SslHelper.getVerificationKey(SslHelper.certificate, certificate).substring(8);
|
||||
|
||||
Notification noti = new NotificationCompat.Builder(getContext(), NotificationHelper.Channels.DEFAULT)
|
||||
.setContentTitle(res.getString(R.string.pairing_request_from, getName()))
|
||||
.setContentText(res.getString(R.string.tap_to_answer))
|
||||
.setContentIntent(pendingIntent)
|
||||
.setContentText(res.getString(R.string.pairing_verification_code, verificationKeyShort))
|
||||
.setTicker(res.getString(R.string.pair_requested))
|
||||
.setSmallIcon(R.drawable.ic_notification)
|
||||
.addAction(R.drawable.ic_accept_pairing_24dp, res.getString(R.string.pairing_accept), acceptedPendingIntent)
|
||||
|
@ -30,6 +30,7 @@ import org.spongycastle.cert.jcajce.JcaX509v3CertificateBuilder;
|
||||
import org.spongycastle.jce.provider.BouncyCastleProvider;
|
||||
import org.spongycastle.operator.ContentSigner;
|
||||
import org.spongycastle.operator.jcajce.JcaContentSignerBuilder;
|
||||
import org.spongycastle.util.Arrays;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.math.BigInteger;
|
||||
@ -254,7 +255,7 @@ public class SslHelper {
|
||||
|
||||
public static String getCertificateHash(Certificate certificate) {
|
||||
try {
|
||||
byte[] hash = MessageDigest.getInstance("SHA-1").digest(certificate.getEncoded());
|
||||
byte[] hash = MessageDigest.getInstance("SHA-256").digest(certificate.getEncoded());
|
||||
Formatter formatter = new Formatter();
|
||||
int i;
|
||||
for (i = 0; i < hash.length - 1; i++) {
|
||||
@ -279,4 +280,31 @@ public class SslHelper {
|
||||
return IETFUtils.valueToString(rdn.getFirst().getValue());
|
||||
}
|
||||
|
||||
public static String getVerificationKey(X509Certificate certificateA, Certificate certificateB) {
|
||||
try {
|
||||
byte[] a = certificateA.getPublicKey().getEncoded();
|
||||
byte[] b = certificateB.getPublicKey().getEncoded();
|
||||
|
||||
if (Arrays.compareUnsigned(a, b) < 0) {
|
||||
// Swap them so on both devices they are in the same order
|
||||
byte[] aux = a;
|
||||
a = b;
|
||||
b = aux;
|
||||
}
|
||||
|
||||
byte[] concat = new byte[a.length + b.length];
|
||||
System.arraycopy(a, 0, concat, 0, a.length);
|
||||
System.arraycopy(b, 0, concat, a.length, b.length);
|
||||
|
||||
byte[] hash = MessageDigest.getInstance("SHA-256").digest(concat);
|
||||
Formatter formatter = new Formatter();
|
||||
for (int i = 0; i < hash.length - 1; i++) {
|
||||
formatter.format("%02x", hash[i]);
|
||||
}
|
||||
return formatter.toString();
|
||||
} catch(Exception e) {
|
||||
e.printStackTrace();
|
||||
return "error";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -104,6 +104,8 @@ public class DeviceFragment extends Fragment {
|
||||
binding.pairButton.setOnClickListener(v -> {
|
||||
binding.pairButton.setVisibility(View.GONE);
|
||||
binding.pairMessage.setText("");
|
||||
binding.pairVerification.setVisibility(View.VISIBLE);
|
||||
binding.pairVerification.setText(SslHelper.getVerificationKey(SslHelper.certificate, device.certificate));
|
||||
binding.pairProgress.setVisibility(View.VISIBLE);
|
||||
BackgroundService.RunCommand(mActivity, service -> {
|
||||
device = service.getDevice(mDeviceId);
|
||||
@ -259,6 +261,8 @@ public class DeviceFragment extends Fragment {
|
||||
public void run() {
|
||||
if (device.isPairRequestedByPeer()) {
|
||||
binding.pairMessage.setText(R.string.pair_requested);
|
||||
binding.pairVerification.setVisibility(View.VISIBLE);
|
||||
binding.pairVerification.setText(SslHelper.getVerificationKey(SslHelper.certificate, device.certificate));
|
||||
binding.pairingButtons.setVisibility(View.VISIBLE);
|
||||
binding.pairProgress.setVisibility(View.GONE);
|
||||
binding.pairButton.setVisibility(View.GONE);
|
||||
@ -333,6 +337,8 @@ public class DeviceFragment extends Fragment {
|
||||
mActivity.runOnUiThread(() -> {
|
||||
if (binding.getRoot() == null) return;
|
||||
binding.pairMessage.setText(error);
|
||||
binding.pairVerification.setText("");
|
||||
binding.pairVerification.setVisibility(View.GONE);
|
||||
binding.pairProgress.setVisibility(View.GONE);
|
||||
binding.pairButton.setVisibility(View.VISIBLE);
|
||||
binding.pairRequestButtons.setVisibility(View.GONE);
|
||||
@ -345,6 +351,7 @@ public class DeviceFragment extends Fragment {
|
||||
mActivity.runOnUiThread(() -> {
|
||||
if (binding.getRoot() == null) return;
|
||||
binding.pairMessage.setText(R.string.device_not_paired);
|
||||
binding.pairVerification.setVisibility(View.GONE);
|
||||
binding.pairProgress.setVisibility(View.GONE);
|
||||
binding.pairButton.setVisibility(View.VISIBLE);
|
||||
binding.pairRequestButtons.setVisibility(View.GONE);
|
||||
|
Loading…
x
Reference in New Issue
Block a user