pull/2/head
scito 4 years ago
commit 750d063a88

@ -0,0 +1,8 @@
root = true
[*]
indent_style = space
indent_size = 4
charset = utf-8
trim_trailing_whitespace = true
insert_final_newline = true

@ -0,0 +1,4 @@
# 2FA example from https://www.raspberrypi.org/blog/setting-up-two-factor-authentication-on-your-raspberry-pi/
# Secret key: 7KSQL2JTUDIS5EF65KLMRQIIGY
# otpauth://totp/pi@raspberrypi?secret=7KSQL2JTUDIS5EF65KLMRQIIGY&issuer=raspberrypi
otpauth-migration://offline?data=CjUKEPqlBekzoNEukL7qlsjBCDYSDnBpQHJhc3BiZXJyeXBpGgtyYXNwYmVycnlwaSABKAEwAhABGAEgACjr4JKK%2B%2F%2F%2F%2F%2F8B

@ -0,0 +1,100 @@
# Extract two-factor autentication (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
# 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 -q 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 defintion):
# 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
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('--qr', '-q', help='print QR codes (otpauth://...)', 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()
verbose = args.verbose
if args.qr:
from qrcode import QRCode
# 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 print_qr(data):
qr = QRCode()
qr.add_data(data)
qr.print_tty()
for line in (line.strip() for line in fileinput.input(args.infile)):
if verbose: print(line)
if line.startswith('#'): continue
parsed_url = urlparse(line)
params = parse_qs(parsed_url.query)
data_encoded = params['data'][0]
data = base64.b64decode(data_encoded)
payload = generated_python.google_auth_pb2.MigrationPayload()
payload.ParseFromString(data)
if verbose: print(payload)
# pylint: disable=no-member
for otp in payload.otp_parameters:
print('\nName: {}'.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', otp.name) + urlencode(url_params)
if args.qr:
if verbose: print(otp_url)
print_qr(otp_url)

@ -0,0 +1,242 @@
# -*- coding: utf-8 -*-
# Generated by the protocol buffer compiler. DO NOT EDIT!
# source: google_auth.proto
from google.protobuf import descriptor as _descriptor
from google.protobuf import message as _message
from google.protobuf import reflection as _reflection
from google.protobuf import symbol_database as _symbol_database
# @@protoc_insertion_point(imports)
_sym_db = _symbol_database.Default()
DESCRIPTOR = _descriptor.FileDescriptor(
name='google_auth.proto',
package='',
syntax='proto3',
serialized_options=None,
create_key=_descriptor._internal_create_key,
serialized_pb=b'\n\x11google_auth.proto\"\xb7\x03\n\x10MigrationPayload\x12\x37\n\x0eotp_parameters\x18\x01 \x03(\x0b\x32\x1f.MigrationPayload.OtpParameters\x12\x0f\n\x07version\x18\x02 \x01(\x05\x12\x12\n\nbatch_size\x18\x03 \x01(\x05\x12\x13\n\x0b\x62\x61tch_index\x18\x04 \x01(\x05\x12\x10\n\x08\x62\x61tch_id\x18\x05 \x01(\x05\x1a\xb7\x01\n\rOtpParameters\x12\x0e\n\x06secret\x18\x01 \x01(\x0c\x12\x0c\n\x04name\x18\x02 \x01(\t\x12\x0e\n\x06issuer\x18\x03 \x01(\t\x12.\n\talgorithm\x18\x04 \x01(\x0e\x32\x1b.MigrationPayload.Algorithm\x12\x0e\n\x06\x64igits\x18\x05 \x01(\x05\x12\'\n\x04type\x18\x06 \x01(\x0e\x32\x19.MigrationPayload.OtpType\x12\x0f\n\x07\x63ounter\x18\x07 \x01(\x03\",\n\tAlgorithm\x12\x10\n\x0c\x41LGO_INVALID\x10\x00\x12\r\n\tALGO_SHA1\x10\x01\"6\n\x07OtpType\x12\x0f\n\x0bOTP_INVALID\x10\x00\x12\x0c\n\x08OTP_HOTP\x10\x01\x12\x0c\n\x08OTP_TOTP\x10\x02\x62\x06proto3'
)
_MIGRATIONPAYLOAD_ALGORITHM = _descriptor.EnumDescriptor(
name='Algorithm',
full_name='MigrationPayload.Algorithm',
filename=None,
file=DESCRIPTOR,
create_key=_descriptor._internal_create_key,
values=[
_descriptor.EnumValueDescriptor(
name='ALGO_INVALID', index=0, number=0,
serialized_options=None,
type=None,
create_key=_descriptor._internal_create_key),
_descriptor.EnumValueDescriptor(
name='ALGO_SHA1', index=1, number=1,
serialized_options=None,
type=None,
create_key=_descriptor._internal_create_key),
],
containing_type=None,
serialized_options=None,
serialized_start=361,
serialized_end=405,
)
_sym_db.RegisterEnumDescriptor(_MIGRATIONPAYLOAD_ALGORITHM)
_MIGRATIONPAYLOAD_OTPTYPE = _descriptor.EnumDescriptor(
name='OtpType',
full_name='MigrationPayload.OtpType',
filename=None,
file=DESCRIPTOR,
create_key=_descriptor._internal_create_key,
values=[
_descriptor.EnumValueDescriptor(
name='OTP_INVALID', index=0, number=0,
serialized_options=None,
type=None,
create_key=_descriptor._internal_create_key),
_descriptor.EnumValueDescriptor(
name='OTP_HOTP', index=1, number=1,
serialized_options=None,
type=None,
create_key=_descriptor._internal_create_key),
_descriptor.EnumValueDescriptor(
name='OTP_TOTP', index=2, number=2,
serialized_options=None,
type=None,
create_key=_descriptor._internal_create_key),
],
containing_type=None,
serialized_options=None,
serialized_start=407,
serialized_end=461,
)
_sym_db.RegisterEnumDescriptor(_MIGRATIONPAYLOAD_OTPTYPE)
_MIGRATIONPAYLOAD_OTPPARAMETERS = _descriptor.Descriptor(
name='OtpParameters',
full_name='MigrationPayload.OtpParameters',
filename=None,
file=DESCRIPTOR,
containing_type=None,
create_key=_descriptor._internal_create_key,
fields=[
_descriptor.FieldDescriptor(
name='secret', full_name='MigrationPayload.OtpParameters.secret', index=0,
number=1, type=12, cpp_type=9, label=1,
has_default_value=False, default_value=b"",
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key),
_descriptor.FieldDescriptor(
name='name', full_name='MigrationPayload.OtpParameters.name', index=1,
number=2, type=9, cpp_type=9, label=1,
has_default_value=False, default_value=b"".decode('utf-8'),
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key),
_descriptor.FieldDescriptor(
name='issuer', full_name='MigrationPayload.OtpParameters.issuer', index=2,
number=3, type=9, cpp_type=9, label=1,
has_default_value=False, default_value=b"".decode('utf-8'),
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key),
_descriptor.FieldDescriptor(
name='algorithm', full_name='MigrationPayload.OtpParameters.algorithm', index=3,
number=4, type=14, cpp_type=8, label=1,
has_default_value=False, default_value=0,
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key),
_descriptor.FieldDescriptor(
name='digits', full_name='MigrationPayload.OtpParameters.digits', index=4,
number=5, type=5, cpp_type=1, label=1,
has_default_value=False, default_value=0,
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key),
_descriptor.FieldDescriptor(
name='type', full_name='MigrationPayload.OtpParameters.type', index=5,
number=6, type=14, cpp_type=8, label=1,
has_default_value=False, default_value=0,
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key),
_descriptor.FieldDescriptor(
name='counter', full_name='MigrationPayload.OtpParameters.counter', index=6,
number=7, type=3, cpp_type=2, label=1,
has_default_value=False, default_value=0,
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key),
],
extensions=[
],
nested_types=[],
enum_types=[
],
serialized_options=None,
is_extendable=False,
syntax='proto3',
extension_ranges=[],
oneofs=[
],
serialized_start=176,
serialized_end=359,
)
_MIGRATIONPAYLOAD = _descriptor.Descriptor(
name='MigrationPayload',
full_name='MigrationPayload',
filename=None,
file=DESCRIPTOR,
containing_type=None,
create_key=_descriptor._internal_create_key,
fields=[
_descriptor.FieldDescriptor(
name='otp_parameters', full_name='MigrationPayload.otp_parameters', index=0,
number=1, type=11, cpp_type=10, label=3,
has_default_value=False, default_value=[],
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key),
_descriptor.FieldDescriptor(
name='version', full_name='MigrationPayload.version', index=1,
number=2, type=5, cpp_type=1, label=1,
has_default_value=False, default_value=0,
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key),
_descriptor.FieldDescriptor(
name='batch_size', full_name='MigrationPayload.batch_size', index=2,
number=3, type=5, cpp_type=1, label=1,
has_default_value=False, default_value=0,
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key),
_descriptor.FieldDescriptor(
name='batch_index', full_name='MigrationPayload.batch_index', index=3,
number=4, type=5, cpp_type=1, label=1,
has_default_value=False, default_value=0,
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key),
_descriptor.FieldDescriptor(
name='batch_id', full_name='MigrationPayload.batch_id', index=4,
number=5, type=5, cpp_type=1, label=1,
has_default_value=False, default_value=0,
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key),
],
extensions=[
],
nested_types=[_MIGRATIONPAYLOAD_OTPPARAMETERS, ],
enum_types=[
_MIGRATIONPAYLOAD_ALGORITHM,
_MIGRATIONPAYLOAD_OTPTYPE,
],
serialized_options=None,
is_extendable=False,
syntax='proto3',
extension_ranges=[],
oneofs=[
],
serialized_start=22,
serialized_end=461,
)
_MIGRATIONPAYLOAD_OTPPARAMETERS.fields_by_name['algorithm'].enum_type = _MIGRATIONPAYLOAD_ALGORITHM
_MIGRATIONPAYLOAD_OTPPARAMETERS.fields_by_name['type'].enum_type = _MIGRATIONPAYLOAD_OTPTYPE
_MIGRATIONPAYLOAD_OTPPARAMETERS.containing_type = _MIGRATIONPAYLOAD
_MIGRATIONPAYLOAD.fields_by_name['otp_parameters'].message_type = _MIGRATIONPAYLOAD_OTPPARAMETERS
_MIGRATIONPAYLOAD_ALGORITHM.containing_type = _MIGRATIONPAYLOAD
_MIGRATIONPAYLOAD_OTPTYPE.containing_type = _MIGRATIONPAYLOAD
DESCRIPTOR.message_types_by_name['MigrationPayload'] = _MIGRATIONPAYLOAD
_sym_db.RegisterFileDescriptor(DESCRIPTOR)
MigrationPayload = _reflection.GeneratedProtocolMessageType('MigrationPayload', (_message.Message,), {
'OtpParameters' : _reflection.GeneratedProtocolMessageType('OtpParameters', (_message.Message,), {
'DESCRIPTOR' : _MIGRATIONPAYLOAD_OTPPARAMETERS,
'__module__' : 'google_auth_pb2'
# @@protoc_insertion_point(class_scope:MigrationPayload.OtpParameters)
})
,
'DESCRIPTOR' : _MIGRATIONPAYLOAD,
'__module__' : 'google_auth_pb2'
# @@protoc_insertion_point(class_scope:MigrationPayload)
})
_sym_db.RegisterMessage(MigrationPayload)
_sym_db.RegisterMessage(MigrationPayload.OtpParameters)
# @@protoc_insertion_point(module_scope)

@ -0,0 +1,32 @@
syntax = "proto3";
// Copied from: https://github.com/beemdevelopment/Aegis/blob/master/app/src/main/proto/google_auth.proto
message MigrationPayload {
enum Algorithm {
ALGO_INVALID = 0;
ALGO_SHA1 = 1;
}
enum OtpType {
OTP_INVALID = 0;
OTP_HOTP = 1;
OTP_TOTP = 2;
}
message OtpParameters {
bytes secret = 1;
string name = 2;
string issuer = 3;
Algorithm algorithm = 4;
int32 digits = 5;
OtpType type = 6;
int64 counter = 7;
}
repeated OtpParameters otp_parameters = 1;
int32 version = 2;
int32 batch_size = 3;
int32 batch_index = 4;
int32 batch_id = 5;
}
Loading…
Cancel
Save