Trustedcoin: add Google Authenticator reset

This commit is contained in:
ThomasV 2016-10-01 11:45:43 +02:00
parent dfef56491b
commit 730cbefeb1
2 changed files with 79 additions and 27 deletions

View File

@ -237,11 +237,9 @@ class Plugin(TrustedCoinPlugin):
window.set_main_layout(vbox, next_enabled=False)
next_button.setText(prior_button_text)
return str(email_e.text())
def setup_google_auth(self, window, _id, otp_secret):
def request_otp_dialog(self, window, _id, otp_secret):
vbox = QVBoxLayout()
if otp_secret is not None:
uri = "otpauth://totp/%s?secret=%s"%('trustedcoin.com', otp_secret)
@ -255,7 +253,7 @@ class Plugin(TrustedCoinPlugin):
label = QLabel(
"This wallet is already registered with Trustedcoin. "
"To finalize wallet creation, please enter your Google Authenticator Code. "
"If you do not have this code, delete the wallet file and start a new registration")
)
label.setWordWrap(1)
vbox.addWidget(label)
msg = _('Google Authenticator code:')
@ -268,18 +266,20 @@ class Plugin(TrustedCoinPlugin):
hbox.addWidget(pw)
vbox.addLayout(hbox)
def set_enabled():
window.next_button.setEnabled(len(pw.text()) == 6)
pw.textChanged.connect(set_enabled)
cb_lost = QCheckBox(_("I have lost my Google Authenticator account"))
cb_lost.setToolTip(_("Check this box to request a new secret. You will need to retype your seed."))
vbox.addWidget(cb_lost)
cb_lost.setVisible(otp_secret is None)
def set_enabled():
b = True if cb_lost.isChecked() else len(pw.text()) == 6
window.next_button.setEnabled(b)
pw.textChanged.connect(set_enabled)
cb_lost.toggled.connect(set_enabled)
window.set_main_layout(vbox, next_enabled=False,
raise_on_cancel=False)
return pw.get_amount(), cb_lost.isChecked()
while True:
if not window.set_main_layout(vbox, next_enabled=False,
raise_on_cancel=False):
return False
otp = pw.get_amount()
try:
server.auth(_id, otp)
return True
except:
window.show_message(_('Incorrect password'))
pw.setText('')

View File

@ -74,9 +74,9 @@ class TrustedCoinException(Exception):
self.status_code = status_code
class TrustedCoinCosignerClient(object):
def __init__(self, user_agent=None, base_url='https://api.trustedcoin.com/2/', debug=False):
def __init__(self, user_agent=None, base_url='https://api.trustedcoin.com/2/'):
self.base_url = base_url
self.debug = debug
self.debug = False
self.user_agent = user_agent
def send_request(self, method, relative_url, data=None):
@ -141,13 +141,18 @@ class TrustedCoinCosignerClient(object):
return self.send_request('post', 'cosigner/%s/auth' % quote(id), payload)
def get(self, id):
"""
Attempt to authenticate for a particular cosigner.
:param id: the id of the cosigner
:param otp: the one time password
"""
""" Get billing info """
return self.send_request('get', 'cosigner/%s' % quote(id))
def get_challenge(self, id):
""" Get challenge to reset Google Auth secret """
return self.send_request('get', 'cosigner/%s/otp_secret' % quote(id))
def reset_auth(self, id, challenge, signatures):
""" Reset Google Auth secret """
payload = {'challenge':challenge, 'signatures':signatures}
return self.send_request('post', 'cosigner/%s/otp_secret' % quote(id), payload)
def sign(self, id, transaction, otp):
"""
Attempt to authenticate for a particular cosigner.
@ -478,8 +483,27 @@ class TrustedCoinPlugin(BasePlugin):
except Exception as e:
wizard.show_message(str(e))
return
if not self.setup_google_auth(wizard, short_id, otp_secret):
wizard.show_message("otp error")
self.check_otp(wizard, short_id, otp_secret, xpub3)
def check_otp(self, wizard, short_id, otp_secret, xpub3):
otp, reset = self.request_otp_dialog(wizard, short_id, otp_secret)
if otp:
self.do_auth(wizard, short_id, otp, xpub3)
elif reset:
wizard.opt_bip39 = False
wizard.opt_ext = True
f = lambda seed, is_bip39, is_ext: wizard.run('on_reset_seed', short_id, seed, is_ext, xpub3)
wizard.restore_seed_dialog(run_next=f, test=self.is_valid_seed)
def on_reset_seed(self, wizard, short_id, seed, is_ext, xpub3):
f = lambda passphrase: wizard.run('on_reset_auth', short_id, seed, passphrase, xpub3)
wizard.passphrase_dialog(run_next=f) if is_ext else f('')
def do_auth(self, wizard, short_id, otp, xpub3):
try:
server.auth(short_id, otp)
except:
wizard.show_message(_('Incorrect password'))
return
k3 = keystore.from_xpub(xpub3)
wizard.storage.put('x3/', k3.dump())
@ -488,6 +512,34 @@ class TrustedCoinPlugin(BasePlugin):
wizard.wallet = Wallet_2fa(wizard.storage)
wizard.run('create_addresses')
def on_reset_auth(self, wizard, short_id, seed, passphrase, xpub3):
xprv1, xpub1, xprv2, xpub2 = self.xkeys_from_seed(seed, passphrase)
try:
assert xpub1 == wizard.storage.get('x1/')['xpub']
assert xpub2 == wizard.storage.get('x2/')['xpub']
except:
wizard.show_message(_('Incorrect seed'))
return
r = server.get_challenge(short_id)
challenge = r.get('challenge')
message = 'TRUSTEDCOIN CHALLENGE: ' + challenge
def f(xprv):
from electrum.bitcoin import deserialize_xkey, bip32_private_key, regenerate_key, is_compressed
_, _, _, c, k = deserialize_xkey(xprv)
pk = bip32_private_key([0, 0], k, c)
key = regenerate_key(pk)
compressed = is_compressed(pk)
sig = key.sign_message(message, compressed)
return base64.b64encode(sig)
signatures = [f(x) for x in [xprv1, xprv2]]
r = server.reset_auth(short_id, challenge, signatures)
new_secret = r.get('otp_secret')
if not new_secret:
wizard.show_message(_('Request rejected by server'))
return
self.check_otp(wizard, short_id, new_secret, xpub3)
@hook
def get_action(self, storage):
if storage.get('wallet_type') != '2fa':