-
-
Notifications
You must be signed in to change notification settings - Fork 12
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Owais Shaikh
committed
May 19, 2021
1 parent
e136951
commit 4baa091
Showing
6 changed files
with
629 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,222 @@ | ||
package com.wristkey | ||
import android.content.Context | ||
import android.content.SharedPreferences | ||
import android.content.res.ColorStateList | ||
import android.graphics.Color | ||
import android.os.Build | ||
import android.os.Bundle | ||
import android.support.wearable.activity.WearableActivity | ||
import android.text.method.PasswordTransformationMethod | ||
import android.view.View | ||
import android.widget.* | ||
import androidx.annotation.RequiresApi | ||
import androidx.wear.widget.BoxInsetLayout | ||
import com.google.gson.Gson | ||
import java.util.* | ||
|
||
|
||
class AddActivity : WearableActivity() { | ||
@RequiresApi(Build.VERSION_CODES.N) | ||
override fun onCreate(savedInstanceState: Bundle?) { | ||
super.onCreate(savedInstanceState) | ||
setContentView(R.layout.activity_add) | ||
val boxinsetlayout = findViewById<BoxInsetLayout>(R.id.BoxInsetLayout) | ||
val addAccountLabel = findViewById<TextView>(R.id.AddAccountLabel) | ||
val confirmButton = findViewById<ImageButton>(R.id.AuthenticatorConfirmButton) | ||
val cancelButton = findViewById<ImageButton>(R.id.CancelButton) | ||
val account = findViewById<EditText>(R.id.AccountField) | ||
val sharedSecret = findViewById<EditText>(R.id.SharedSecretField) | ||
sharedSecret.transformationMethod = PasswordTransformationMethod.getInstance() | ||
val modeGroup = findViewById<RadioGroup>(R.id.GeneratorMode) | ||
val timeMode = findViewById<RadioButton>(R.id.TimeMode) | ||
val counterMode = findViewById<RadioButton>(R.id.CounterMode) | ||
modeGroup.check(R.id.TimeMode) | ||
var mode = "Time" | ||
val digitLength = findViewById<SeekBar>(R.id.DigitLengthSeekbar) | ||
digitLength.progress = 1 | ||
var selectedDigitLength = "6" | ||
val digitLengthLabel = findViewById<TextView>(R.id.DigitLength) | ||
val algorithm = findViewById<SeekBar>(R.id.AlgorithmKeylengthSeekbar) | ||
algorithm.progress = 0 | ||
var selectedAlgorithm = "HmacAlgorithm.SHA1" | ||
val algorithmLabel = findViewById<TextView>(R.id.AlgorithmLength) | ||
val appData: SharedPreferences = applicationContext.getSharedPreferences( | ||
appDataFile, | ||
Context.MODE_PRIVATE | ||
) | ||
var currentAccent = appData.getString("accent", "4285F4") | ||
var currentTheme = appData.getString("theme", "000000") | ||
boxinsetlayout.setBackgroundColor(Color.parseColor("#" + currentTheme)) | ||
account.backgroundTintList = ColorStateList.valueOf(Color.parseColor("#" + currentAccent)) | ||
account.foregroundTintList = ColorStateList.valueOf(Color.parseColor("#" + currentAccent)) | ||
account.compoundDrawableTintList = ColorStateList.valueOf(Color.parseColor("#" + currentAccent)) | ||
sharedSecret.backgroundTintList = ColorStateList.valueOf(Color.parseColor("#" + currentAccent)) | ||
sharedSecret.foregroundTintList = ColorStateList.valueOf(Color.parseColor("#" + currentAccent)) | ||
sharedSecret.compoundDrawableTintList = ColorStateList.valueOf(Color.parseColor("#" + currentAccent)) | ||
timeMode.buttonTintList = ColorStateList.valueOf(Color.parseColor("#" + currentAccent)) | ||
counterMode.buttonTintList = ColorStateList.valueOf(Color.parseColor("#" + currentAccent)) | ||
digitLength.backgroundTintList = ColorStateList.valueOf(Color.parseColor("#" + currentAccent)) | ||
digitLength.foregroundTintList = ColorStateList.valueOf(Color.parseColor("#" + currentAccent)) | ||
digitLength.progressTintList = ColorStateList.valueOf(Color.parseColor("#" + currentAccent)) | ||
digitLength.progressBackgroundTintList = ColorStateList.valueOf(Color.parseColor("#" + currentAccent)) | ||
digitLength.secondaryProgressTintList = ColorStateList.valueOf(Color.parseColor("#" + currentAccent)) | ||
digitLength.indeterminateTintList = ColorStateList.valueOf(Color.parseColor("#" + currentAccent)) | ||
digitLength.thumbTintList = ColorStateList.valueOf(Color.parseColor("#" + currentAccent)) | ||
digitLength.tickMarkTintList = ColorStateList.valueOf(Color.parseColor("#" + currentAccent)) | ||
algorithm.backgroundTintList = ColorStateList.valueOf(Color.parseColor("#" + currentAccent)) | ||
algorithm.foregroundTintList = ColorStateList.valueOf(Color.parseColor("#" + currentAccent)) | ||
algorithm.progressTintList = ColorStateList.valueOf(Color.parseColor("#" + currentAccent)) | ||
algorithm.progressBackgroundTintList = ColorStateList.valueOf(Color.parseColor("#" + currentAccent)) | ||
algorithm.secondaryProgressTintList = ColorStateList.valueOf(Color.parseColor("#" + currentAccent)) | ||
algorithm.indeterminateTintList = ColorStateList.valueOf(Color.parseColor("#" + currentAccent)) | ||
algorithm.thumbTintList = ColorStateList.valueOf(Color.parseColor("#" + currentAccent)) | ||
algorithm.tickMarkTintList = ColorStateList.valueOf(Color.parseColor("#" + currentAccent)) | ||
confirmButton.backgroundTintList = ColorStateList.valueOf(Color.parseColor("#" + currentAccent)) | ||
if (currentTheme == "F7F7F7") { | ||
addAccountLabel.setTextColor(ColorStateList.valueOf(Color.parseColor("#000000"))) | ||
account.setHintTextColor(ColorStateList.valueOf(Color.parseColor("#000000"))) | ||
sharedSecret.setHintTextColor(ColorStateList.valueOf(Color.parseColor("#000000"))) | ||
timeMode.setTextColor(ColorStateList.valueOf(Color.parseColor("#000000"))) | ||
counterMode.setTextColor(ColorStateList.valueOf(Color.parseColor("#000000"))) | ||
digitLengthLabel.setTextColor(ColorStateList.valueOf(Color.parseColor("#000000"))) | ||
algorithmLabel.setTextColor(ColorStateList.valueOf(Color.parseColor("#000000"))) | ||
} else { | ||
addAccountLabel.setTextColor(ColorStateList.valueOf(Color.parseColor("#FFFFFF"))) | ||
account.setHintTextColor(ColorStateList.valueOf(Color.parseColor("#FFFFFF"))) | ||
sharedSecret.setHintTextColor(ColorStateList.valueOf(Color.parseColor("#FFFFFF"))) | ||
timeMode.setTextColor(ColorStateList.valueOf(Color.parseColor("#FFFFFF"))) | ||
counterMode.setTextColor(ColorStateList.valueOf(Color.parseColor("#FFFFFF"))) | ||
digitLengthLabel.setTextColor(ColorStateList.valueOf(Color.parseColor("#FFFFFF"))) | ||
algorithmLabel.setTextColor(ColorStateList.valueOf(Color.parseColor("#FFFFFF"))) | ||
} | ||
confirmButton.setOnClickListener { | ||
val errorToast: Toast? | ||
val tokenData = ArrayList<String>() | ||
if (account.text.toString() == ""){ | ||
errorToast = Toast.makeText(this, "Enter account name", Toast.LENGTH_SHORT) | ||
errorToast.show() | ||
}else if (sharedSecret.text.toString() == ""){ | ||
errorToast = Toast.makeText(this, "Enter shared secret", Toast.LENGTH_SHORT) | ||
errorToast.show() | ||
}else if((sharedSecret.text.toString()).length < 8 && selectedDigitLength == "6" && selectedAlgorithm == "HmacAlgorithm.SHA1" && mode == "Time"){ | ||
errorToast = Toast.makeText(this, "Invalid shared secret", Toast.LENGTH_SHORT) | ||
errorToast.show() | ||
}else{ | ||
tokenData.add(account.text.toString()) | ||
tokenData.add(sharedSecret.text.toString()) | ||
tokenData.add(mode) | ||
tokenData.add(selectedDigitLength) | ||
tokenData.add(selectedAlgorithm) | ||
tokenData.add("0") // If counter mode is selected, initial value must be 0. | ||
val json = Gson().toJson(tokenData) | ||
|
||
val id = UUID.randomUUID().toString() | ||
|
||
logins.edit().putString(id, json).apply() | ||
val addedToast = Toast.makeText(this, "Added account", Toast.LENGTH_SHORT) | ||
addedToast.show() | ||
finish() | ||
} | ||
} | ||
cancelButton.setOnClickListener { | ||
finish() | ||
} | ||
modeGroup.setOnCheckedChangeListener { _, checkedId -> | ||
mode = if (checkedId != -1) { | ||
(findViewById<View>(checkedId) as RadioButton).text.toString() | ||
} else { | ||
"" | ||
} | ||
} | ||
digitLength.setOnSeekBarChangeListener(object : SeekBar.OnSeekBarChangeListener { | ||
override fun onProgressChanged(p0: SeekBar?, p1: Int, p2: Boolean) { | ||
if (digitLength.progress == 0) { | ||
digitLengthLabel.text = "4 digits" | ||
selectedDigitLength = "4" | ||
} else if (digitLength.progress == 1) { | ||
selectedDigitLength = "6" | ||
digitLengthLabel.text = "6 digits" | ||
} else if (digitLength.progress == 2) { | ||
selectedDigitLength = "7" | ||
digitLengthLabel.text = "7 digits" | ||
} else if (digitLength.progress == 3) { | ||
selectedDigitLength = "8" | ||
digitLengthLabel.text = "8 digits" | ||
} | ||
} | ||
|
||
override fun onStartTrackingTouch(seekBar: SeekBar?) { | ||
if (digitLength.progress == 0) { | ||
digitLengthLabel.text = "4 digits" | ||
selectedDigitLength = "4" | ||
} else if (digitLength.progress == 1) { | ||
selectedDigitLength = "6" | ||
digitLengthLabel.text = "6 digits" | ||
} else if (digitLength.progress == 2) { | ||
selectedDigitLength = "7" | ||
digitLengthLabel.text = "7 digits" | ||
} else if (digitLength.progress == 3) { | ||
selectedDigitLength = "8" | ||
digitLengthLabel.text = "8 digits" | ||
} | ||
} | ||
|
||
override fun onStopTrackingTouch(seekBar: SeekBar?) { | ||
if (digitLength.progress == 0) { | ||
digitLengthLabel.text = "4 digits" | ||
selectedDigitLength = "4" | ||
} else if (digitLength.progress == 1) { | ||
selectedDigitLength = "6" | ||
digitLengthLabel.text = "6 digits" | ||
} else if (digitLength.progress == 2) { | ||
selectedDigitLength = "7" | ||
digitLengthLabel.text = "7 digits" | ||
} else if (digitLength.progress == 3) { | ||
selectedDigitLength = "8" | ||
digitLengthLabel.text = "8 digits" | ||
} | ||
} | ||
}) | ||
|
||
algorithm.setOnSeekBarChangeListener(object : SeekBar.OnSeekBarChangeListener { | ||
override fun onProgressChanged(p0: SeekBar?, p1: Int, p2: Boolean) { | ||
if (algorithm.progress == 0) { | ||
algorithmLabel.text = "SHA-1" | ||
selectedAlgorithm = "HmacAlgorithm.SHA1" | ||
} else if (algorithm.progress == 1) { | ||
algorithmLabel.text = "SHA-256" | ||
selectedAlgorithm = "HmacAlgorithm.SHA256" | ||
} else if (algorithm.progress == 2) { | ||
algorithmLabel.text = "SHA-512" | ||
selectedAlgorithm = "HmacAlgorithm.SHA512" | ||
} | ||
} | ||
|
||
override fun onStartTrackingTouch(seekBar: SeekBar?) { | ||
if (algorithm.progress == 0) { | ||
algorithmLabel.text = "SHA-1" | ||
selectedAlgorithm = "HmacAlgorithm.SHA1" | ||
} else if (algorithm.progress == 1) { | ||
algorithmLabel.text = "SHA-256" | ||
selectedAlgorithm = "HmacAlgorithm.SHA256" | ||
} else if (algorithm.progress == 2) { | ||
algorithmLabel.text = "SHA-512" | ||
selectedAlgorithm = "HmacAlgorithm.SHA512" | ||
} | ||
} | ||
|
||
override fun onStopTrackingTouch(seekBar: SeekBar?) { | ||
if (algorithm.progress == 0) { | ||
algorithmLabel.text = "SHA-1" | ||
selectedAlgorithm = "HmacAlgorithm.SHA1" | ||
} else if (algorithm.progress == 1) { | ||
algorithmLabel.text = "SHA-256" | ||
selectedAlgorithm = "HmacAlgorithm.SHA256" | ||
} else if (algorithm.progress == 2) { | ||
algorithmLabel.text = "SHA-512" | ||
selectedAlgorithm = "HmacAlgorithm.SHA512" | ||
} | ||
} | ||
}) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,122 @@ | ||
# Extract two-factor authentication (2FA, TFA) secret keys from export QR codes of "Google Authenticator" app | ||
# | ||
# Usage: | ||
# 1. Export the QR codes from "Google Authenticator" app | ||
# 2. Read QR codes with QR code reader (e.g. with a second device) | ||
# 3. Save the captured QR codes in a text file. Save each QR code on a new line. (The captured QR codes look like "otpauth-migration://offline?data=...") | ||
# 4. Call this script with the file as input: | ||
# python extract_otp_secret_keys.py -p example_export.txt | ||
# | ||
# Requirement: | ||
# The protobuf package of Google for proto3 is required for running this script. | ||
# pip install protobuf | ||
# | ||
# Optional: | ||
# For printing QR codes, the qrcode module is required | ||
# pip install qrcode | ||
# | ||
# Technical background: | ||
# The export QR code of "Google Authenticator" contains the URL "otpauth-migration://offline?data=...". | ||
# The data parameter is a base64 encoded proto3 message (Google Protocol Buffers). | ||
# | ||
# Command for regeneration of Python code from proto3 message definition file (only necessary in case of changes of the proto3 message definition): | ||
# protoc --python_out=generated_python google_auth.proto | ||
# | ||
# References: | ||
# Proto3 documentation: https://developers.google.com/protocol-buffers/docs/pythontutorial | ||
# Template code: https://github.com/beemdevelopment/Aegis/pull/406 | ||
|
||
# Author: Scito (https://scito.ch) | ||
|
||
# This program is free software: you can redistribute it and/or modify | ||
# it under the terms of the GNU General Public License as published by | ||
# the Free Software Foundation, either version 3 of the License, or | ||
# (at your option) any later version. | ||
# | ||
# This program is distributed in the hope that it will be useful, | ||
# but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
# GNU General Public License for more details. | ||
# | ||
# You should have received a copy of the GNU General Public License | ||
# along with this program. If not, see <https://www.gnu.org/licenses/>. | ||
|
||
import argparse | ||
import base64 | ||
import fileinput | ||
import sys | ||
from urllib.parse import parse_qs, urlencode, urlparse, quote | ||
from os import path, mkdir | ||
from re import sub, compile as rcompile | ||
import generated_python.google_auth_pb2 | ||
|
||
arg_parser = argparse.ArgumentParser() | ||
arg_parser.add_argument('--verbose', '-v', help='verbose output', action='store_true') | ||
arg_parser.add_argument('--saveqr', '-s', help='save QR code(s) as images to the "qr" subfolder', action='store_true') | ||
arg_parser.add_argument('--printqr', '-p', help='print QR code(s) as text to the terminal', action='store_true') | ||
arg_parser.add_argument('infile', help='file or - for stdin (default: -) with "otpauth-migration://..." URLs separated by newlines, lines starting with # are ignored') | ||
args = arg_parser.parse_args() | ||
|
||
if args.saveqr or args.printqr: from qrcode import QRCode | ||
verbose = args.verbose | ||
|
||
# https://stackoverflow.com/questions/40226049/find-enums-listed-in-python-descriptor-for-protobuf | ||
def get_enum_name_by_number(parent, field_name): | ||
field_value = getattr(parent, field_name) | ||
return parent.DESCRIPTOR.fields_by_name[field_name].enum_type.values_by_number.get(field_value).name | ||
|
||
def convert_secret_from_bytes_to_base32_str(bytes): | ||
return str(base64.b32encode(otp.secret), 'utf-8').replace('=', '') | ||
|
||
def save_qr(data, name): | ||
qr = QRCode() | ||
qr.add_data(data) | ||
img = qr.make_image(fill_color='black', back_color='white') | ||
if verbose: print('Saving to {}'.format(name)) | ||
img.save(name) | ||
|
||
def print_qr(data): | ||
qr = QRCode() | ||
qr.add_data(data) | ||
qr.print_tty() | ||
|
||
i = j = 0 | ||
for line in (line.strip() for line in fileinput.input(args.infile)): | ||
if verbose: print(line) | ||
if line.startswith('#') or line == '': continue | ||
if not line.startswith('otpauth-migration://'): print('\nWARN: line is not a otpauth-migration:// URL\ninput file: {}\nline "{}"\nProbably a wrong file was given'.format(args.infile, line)) | ||
parsed_url = urlparse(line) | ||
params = parse_qs(parsed_url.query) | ||
if not 'data' in params: | ||
print('\nERROR: no data query parameter in input URL\ninput file: {}\nline "{}"\nProbably a wrong file was given'.format(args.infile, line)) | ||
sys.exit(1) | ||
data_encoded = params['data'][0] | ||
data = base64.b64decode(data_encoded) | ||
payload = generated_python.google_auth_pb2.MigrationPayload() | ||
payload.ParseFromString(data) | ||
i += 1 | ||
if verbose: print('\n{}. Payload Line'.format(i), payload, sep='\n') | ||
|
||
# pylint: disable=no-member | ||
for otp in payload.otp_parameters: | ||
j += 1 | ||
if verbose: print('\n{}. Secret Key'.format(j)) | ||
else: print() | ||
print('Name: {}'.format(otp.name)) | ||
secret = convert_secret_from_bytes_to_base32_str(otp.secret) | ||
print('Secret: {}'.format(secret)) | ||
if otp.issuer: print('Issuer: {}'.format(otp.issuer)) | ||
print('Type: {}'.format(get_enum_name_by_number(otp, 'type'))) | ||
url_params = { 'secret': secret } | ||
if otp.type == 1: url_params['counter'] = otp.counter | ||
if otp.issuer: url_params['issuer'] = otp.issuer | ||
otp_url = 'otpauth://{}/{}?'.format('totp' if otp.type == 2 else 'hotp', quote(otp.name)) + urlencode(url_params) | ||
if verbose: print(otp_url) | ||
if args.printqr: | ||
print_qr(otp_url) | ||
if args.saveqr: | ||
if not(path.exists('qr')): mkdir('qr') | ||
pattern = rcompile(r'[\W_]+') | ||
file_otp_name = pattern.sub('', otp.name) | ||
file_otp_issuer = pattern.sub('', otp.issuer) | ||
save_qr(otp_url, 'qr/{}-{}{}.png'.format(j, file_otp_name, '-' + file_otp_issuer if file_otp_issuer else '')) |
File renamed without changes.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
<vector android:height="24dp" android:tint="#FFFFFF" | ||
android:viewportHeight="24" android:viewportWidth="24" | ||
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android"> | ||
<path android:fillColor="@android:color/white" android:pathData="M3,17.25V21h3.75L17.81,9.94l-3.75,-3.75L3,17.25zM20.71,7.04c0.39,-0.39 0.39,-1.02 0,-1.41l-2.34,-2.34c-0.39,-0.39 -1.02,-0.39 -1.41,0l-1.83,1.83 3.75,3.75 1.83,-1.83z"/> | ||
</vector> |
Oops, something went wrong.