[PATCH] Share a secret key by displaying a QR code.
by Kai-Chieh Ku
This patch implements the ability to share
secret keys via QR codes.
Use scenarios:
1. "Team account": Sometimes a group of people may need to
share the same credential as well as the token.
2. To transfer the token across devices,
for instance, users will get new devices in years.
There may be some security concerns,
but it is not that dangerous IMO.
Any advises to get this merged are welcome.
Thanks.
---
app/src/main/AndroidManifest.xml | 4 ++
.../org/fedorahosted/freeotp/TokenAdapter.java | 9 ++-
.../freeotp/share/ShowBarcodeActivity.java | 67 ++++++++++++++++++++++
app/src/main/res/layout/show_barcode.xml | 16 ++++++
app/src/main/res/menu/token.xml | 6 ++
app/src/main/res/values/strings.xml | 2 +
6 files changed, 103 insertions(+), 1 deletion(-)
create mode 100644 app/src/main/java/org/fedorahosted/freeotp/share/ShowBarcodeActivity.java
create mode 100644 app/src/main/res/layout/show_barcode.xml
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index fd6c7a5..3a73615 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -78,6 +78,10 @@
/>
<activity
+ android:name=".share.ShowBarcodeActivity"
+ android:label="@string/share_via_barcode"
+ />
+ <activity
android:name=".MainActivity"
android:label="@string/app_name"
android:clearTaskOnLaunch="true"
diff --git a/app/src/main/java/org/fedorahosted/freeotp/TokenAdapter.java b/app/src/main/java/org/fedorahosted/freeotp/TokenAdapter.java
index e7adf36..029dab2 100644
--- a/app/src/main/java/org/fedorahosted/freeotp/TokenAdapter.java
+++ b/app/src/main/java/org/fedorahosted/freeotp/TokenAdapter.java
@@ -34,6 +34,7 @@ import android.widget.Toast;
import org.fedorahosted.freeotp.edit.DeleteActivity;
import org.fedorahosted.freeotp.edit.EditActivity;
+import org.fedorahosted.freeotp.share.ShowBarcodeActivity;
import java.util.HashMap;
import java.util.Map;
@@ -87,7 +88,7 @@ public class TokenAdapter extends BaseReorderableAdapter {
protected void bindView(View view, final int position) {
final Context ctx = view.getContext();
TokenLayout tl = (TokenLayout) view;
- Token token = getItem(position);
+ final Token token = getItem(position);
tl.bind(token, R.menu.token, new PopupMenu.OnMenuItemClickListener() {
@Override
@@ -106,6 +107,12 @@ public class TokenAdapter extends BaseReorderableAdapter {
i.putExtra(DeleteActivity.EXTRA_POSITION, position);
ctx.startActivity(i);
break;
+
+ case R.id.action_share_via_barcode:
+ i = new Intent(ctx, ShowBarcodeActivity.class);
+ i.setData(token.toUri());
+ ctx.startActivity(i);
+ break;
}
return true;
diff --git a/app/src/main/java/org/fedorahosted/freeotp/share/ShowBarcodeActivity.java b/app/src/main/java/org/fedorahosted/freeotp/share/ShowBarcodeActivity.java
new file mode 100644
index 0000000..211c934
--- /dev/null
+++ b/app/src/main/java/org/fedorahosted/freeotp/share/ShowBarcodeActivity.java
@@ -0,0 +1,67 @@
+package org.fedorahosted.freeotp.share;
+
+import android.app.Activity;
+import android.graphics.Bitmap;
+import android.graphics.Color;
+import android.graphics.drawable.BitmapDrawable;
+import android.net.Uri;
+import android.os.Bundle;
+import android.util.Log;
+import android.view.WindowManager;
+import android.widget.ImageView;
+
+import com.google.zxing.BarcodeFormat;
+import com.google.zxing.MultiFormatWriter;
+import com.google.zxing.WriterException;
+import com.google.zxing.common.BitMatrix;
+
+import org.fedorahosted.freeotp.R;
+
+public class ShowBarcodeActivity extends Activity {
+ private static final String TAG = "ShowBarcodeActivity";
+
+ private static final int DIMENSION = 400;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ Uri data = getIntent().getData();
+ if (data == null) {
+ finish(); // Disallow any spurious usage.
+ return;
+ }
+
+ // Don't permit screenshots since it contains the secret key
+ getWindow().setFlags(WindowManager.LayoutParams.FLAG_SECURE, WindowManager.LayoutParams.FLAG_SECURE);
+
+ setContentView(R.layout.show_barcode);
+
+ try {
+ Bitmap bitmap = createBarcode(data);
+ ImageView view = (ImageView)findViewById(R.id.barcode);
+ view.setImageDrawable(new BitmapDrawable(getResources(), bitmap));
+ } catch (WriterException e) {
+ Log.e(TAG, "", e);
+ finish();
+ }
+ }
+
+ private static Bitmap createBarcode(Uri data) throws WriterException {
+ BitMatrix result = new MultiFormatWriter().encode(data.toString(), BarcodeFormat.QR_CODE, DIMENSION, DIMENSION);
+
+ int width = result.getWidth();
+ int height = result.getHeight();
+ int[] pixels = new int[width * height];
+ for (int y = 0; y < height; y++) {
+ int offset = y * width;
+ for (int x = 0; x < width; x++) {
+ pixels[offset + x] = result.get(x, y) ? Color.BLACK : Color.WHITE;
+ }
+ }
+
+ Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
+ bitmap.setPixels(pixels, 0, width, 0, 0, width, height);
+ return bitmap;
+ }
+}
diff --git a/app/src/main/res/layout/show_barcode.xml b/app/src/main/res/layout/show_barcode.xml
new file mode 100644
index 0000000..1f0bd74
--- /dev/null
+++ b/app/src/main/res/layout/show_barcode.xml
@@ -0,0 +1,16 @@
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:tools="http://schemas.android.com/tools"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:padding="60dp"
+ android:background="@android:color/black"
+ tools:context="org.fedorahosted.freeotp.share.ShowBarcodeActivity">
+
+ <ImageView
+ android:id="@+id/barcode"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:layout_centerInParent="true"
+ android:scaleType="fitCenter"/>
+
+</RelativeLayout>
diff --git a/app/src/main/res/menu/token.xml b/app/src/main/res/menu/token.xml
index a5a28e9..237dd89 100644
--- a/app/src/main/res/menu/token.xml
+++ b/app/src/main/res/menu/token.xml
@@ -30,4 +30,10 @@
android:icon="@android:drawable/ic_menu_delete"
android:title="@string/delete"
/>
+
+ <item
+ android:id="@+id/action_share_via_barcode"
+ android:icon="@android:drawable/ic_menu_share"
+ android:title="@string/share_via_barcode"
+ />
</menu>
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index de39f41..c63c9d9 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -1,10 +1,12 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
+
<string name="app_name">FreeOTP</string>
<string name="add">Add</string>
<string name="invalid_token">The token specified was invalid!</string>
<string name="delete">Delete</string>
<string name="edit">Edit</string>
+ <string name="share_via_barcode">Share via Barcode</string>
<string name="restore_defaults">Restore Defaults</string>
<string name="no_keys">No OTP keys installed.</string>
<string name="add_token">Add Token</string>
--
1.8.5.5
9 years, 1 month
How to localize
by Gao Yongwei
Hello,
I want to localize freeotp and I remember that there are some introductions
on how to do this ,but I cannot find the links , :-(
--
*Regards,*
*GaoYongwei*
9 years, 3 months