Skip to content
This repository was archived by the owner on Sep 13, 2024. It is now read-only.

Commit d3939b5

Browse files
Finished OTP implementation #5
1 parent 005f962 commit d3939b5

6 files changed

Lines changed: 112 additions & 8 deletions

File tree

0 Bytes
Binary file not shown.

app/build.gradle

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ android {
2020

2121
dependencies {
2222
implementation fileTree(dir: 'libs', include: ['*.jar'])
23-
implementation 'com.android.support:appcompat-v7:27.0.0'
23+
implementation 'com.android.support:appcompat-v7:27.1.1'
2424
implementation 'com.android.support.constraint:constraint-layout:1.1.2'
2525
implementation 'com.android.support:support-v4:27.1.1'
2626
implementation 'com.journeyapps:zxing-android-embedded:3.6.0'

app/src/main/java/com/coderbunker/kioskapp/KioskActivity.java

Lines changed: 20 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
import android.app.Dialog;
55
import android.content.Context;
66
import android.content.DialogInterface;
7+
import android.content.SharedPreferences;
78
import android.content.pm.ActivityInfo;
89
import android.os.Bundle;
910
import android.view.KeyEvent;
@@ -13,6 +14,9 @@
1314
import android.view.WindowManager;
1415
import android.webkit.WebView;
1516
import android.widget.Button;
17+
import android.widget.Toast;
18+
19+
import com.coderbunker.kioskapp.lib.TOTP;
1620

1721
import java.util.ArrayList;
1822
import java.util.Arrays;
@@ -43,6 +47,8 @@ public class KioskActivity extends Activity {
4347

4448
private Timer timerLock, timerNav;
4549

50+
private SharedPreferences prefs;
51+
4652
@Override
4753
public void onBackPressed() {
4854
//Do nothing...
@@ -87,6 +93,9 @@ public boolean onTouch(View view, MotionEvent motionEvent) {
8793
}
8894
});
8995

96+
prefs = this.getSharedPreferences(
97+
"com.coderbunker.kioskapp", Context.MODE_PRIVATE);
98+
9099
TimerTask lock = new TimerTask() {
91100
@Override
92101
public void run() {
@@ -180,13 +189,18 @@ public void enterNumber(String number) {
180189
}
181190

182191
private void checkPwd() {
183-
String pwd = b1.getText().toString() + b2.getText().toString() + b3.getText().toString() + b4.getText().toString();
184-
if (password.equals(pwd)) {
185-
finish();
192+
String otp = prefs.getString("otp", null);
193+
String pwd = b1.getText().toString() + b2.getText().toString() + b3.getText().toString() + b4.getText().toString() + b5.getText().toString() + b6.getText().toString();
194+
String generated_number = TOTP.generateCurrentNumber(otp, System.currentTimeMillis());
195+
String previous_generated_number = TOTP.generateCurrentNumber(otp, System.currentTimeMillis() - 30000);
186196

197+
if (pwd.equals(generated_number) || pwd.equals(previous_generated_number)) {
198+
Toast.makeText(context, "PIN correct", Toast.LENGTH_SHORT).show();
199+
finish();
187200
} else {
188201
dialog.dismiss();
189202
dialogPrompted = false;
203+
Toast.makeText(context, "Wrong PIN", Toast.LENGTH_SHORT).show();
190204
}
191205

192206
cptPwd = 0;
@@ -205,8 +219,10 @@ public boolean dispatchKeyEvent(KeyEvent event) {
205219

206220
@Override
207221
public boolean onKeyDown(int keyCode, KeyEvent event) {
222+
Toast.makeText(context, "Clicked", Toast.LENGTH_SHORT).show();
208223
if (blockedKeys.contains(event.getKeyCode())) {
209-
return true;
224+
Toast.makeText(context, "Blocked", Toast.LENGTH_SHORT).show();
225+
return false;
210226
}
211227
return super.onKeyDown(keyCode, event);
212228
}

app/src/main/java/com/coderbunker/kioskapp/MainActivity.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,8 @@ protected void onCreate(Bundle savedInstanceState) {
1616
super.onCreate(savedInstanceState);
1717
setContentView(R.layout.activity_main);
1818

19-
kioskmode = (Button) findViewById(R.id.button_kioskmode);
20-
settings = (Button) findViewById(R.id.button_settings);
19+
kioskmode = findViewById(R.id.button_kioskmode);
20+
settings = findViewById(R.id.button_settings);
2121

2222
kioskmode.setOnClickListener(new View.OnClickListener() {
2323
@Override

app/src/main/java/com/coderbunker/kioskapp/SettingsActivity.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,7 @@ public void onClick(View view) {
8585
editor.apply();
8686
}
8787

88-
otp_uri = "otpauth://totp/Admin:coderbunker.com?secret=" + otp + "&issuer=Coderbunker";
88+
otp_uri = "otpauth://totp/Admin?secret=" + otp + "&issuer=Coderbunker";
8989

9090
generateQRCode(otp_uri);
9191
}
Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
package com.coderbunker.kioskapp.lib;
2+
3+
import java.security.InvalidKeyException;
4+
import java.security.NoSuchAlgorithmException;
5+
6+
import javax.crypto.Mac;
7+
import javax.crypto.spec.SecretKeySpec;
8+
9+
public class TOTP {
10+
11+
private static int TIME_STEP_SECONDS = 30, NUM_DIGITS_OUTPUT = 6;
12+
13+
//Source: https://github.com/airG/java-totp/blob/master/src/main/java/com/j256/totp/TwoFactorAuthUtil.java
14+
15+
public static String generateCurrentNumber(String secret, long currentTimeMillis) {
16+
17+
byte[] key = new byte[0];
18+
try {
19+
key = Base32.decode(secret);
20+
} catch (Base32.DecodingException e) {
21+
e.printStackTrace();
22+
}
23+
24+
byte[] data = new byte[8];
25+
long value = currentTimeMillis / 1000 / TIME_STEP_SECONDS;
26+
for (int i = 7; value > 0; i--) {
27+
data[i] = (byte) (value & 0xFF);
28+
value >>= 8;
29+
}
30+
31+
// encrypt the data with the key and return the SHA1 of it in hex
32+
SecretKeySpec signKey = new SecretKeySpec(key, "HmacSHA1");
33+
// if this is expensive, could put in a thread-local
34+
Mac mac = null;
35+
try {
36+
mac = Mac.getInstance("HmacSHA1");
37+
} catch (NoSuchAlgorithmException e) {
38+
e.printStackTrace();
39+
}
40+
try {
41+
mac.init(signKey);
42+
} catch (InvalidKeyException e) {
43+
e.printStackTrace();
44+
}
45+
byte[] hash = mac.doFinal(data);
46+
47+
// take the 4 least significant bits from the encrypted string as an offset
48+
int offset = hash[hash.length - 1] & 0xF;
49+
50+
// We're using a long because Java hasn't got unsigned int.
51+
long truncatedHash = 0;
52+
for (int i = offset; i < offset + 4; ++i) {
53+
truncatedHash <<= 8;
54+
// get the 4 bytes at the offset
55+
truncatedHash |= (hash[i] & 0xFF);
56+
}
57+
// cut off the top bit
58+
truncatedHash &= 0x7FFFFFFF;
59+
60+
// the token is then the last 6 digits in the number
61+
truncatedHash %= 1000000;
62+
63+
return zeroPrepend(truncatedHash, NUM_DIGITS_OUTPUT);
64+
}
65+
66+
private static String zeroPrepend(long num, int digits) {
67+
String hashStr = Long.toString(num);
68+
if (hashStr.length() >= digits) {
69+
return hashStr;
70+
} else {
71+
StringBuilder sb = new StringBuilder(digits);
72+
int zeroCount = digits - hashStr.length();
73+
sb.append(blockOfZeros, 0, zeroCount);
74+
sb.append(hashStr);
75+
return sb.toString();
76+
}
77+
}
78+
79+
private static String blockOfZeros = null;
80+
81+
{
82+
StringBuilder sb = new StringBuilder(NUM_DIGITS_OUTPUT);
83+
for (int i = 0; i < NUM_DIGITS_OUTPUT; i++) {
84+
sb.append('0');
85+
}
86+
blockOfZeros = sb.toString();
87+
}
88+
}

0 commit comments

Comments
 (0)