Trustedcoin: add Google Authenticator reset
This commit is contained in:
parent
dfef56491b
commit
730cbefeb1
|
@ -237,11 +237,9 @@ class Plugin(TrustedCoinPlugin):
|
||||||
|
|
||||||
window.set_main_layout(vbox, next_enabled=False)
|
window.set_main_layout(vbox, next_enabled=False)
|
||||||
next_button.setText(prior_button_text)
|
next_button.setText(prior_button_text)
|
||||||
|
|
||||||
return str(email_e.text())
|
return str(email_e.text())
|
||||||
|
|
||||||
|
def request_otp_dialog(self, window, _id, otp_secret):
|
||||||
def setup_google_auth(self, window, _id, otp_secret):
|
|
||||||
vbox = QVBoxLayout()
|
vbox = QVBoxLayout()
|
||||||
if otp_secret is not None:
|
if otp_secret is not None:
|
||||||
uri = "otpauth://totp/%s?secret=%s"%('trustedcoin.com', otp_secret)
|
uri = "otpauth://totp/%s?secret=%s"%('trustedcoin.com', otp_secret)
|
||||||
|
@ -255,7 +253,7 @@ class Plugin(TrustedCoinPlugin):
|
||||||
label = QLabel(
|
label = QLabel(
|
||||||
"This wallet is already registered with Trustedcoin. "
|
"This wallet is already registered with Trustedcoin. "
|
||||||
"To finalize wallet creation, please enter your Google Authenticator Code. "
|
"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)
|
label.setWordWrap(1)
|
||||||
vbox.addWidget(label)
|
vbox.addWidget(label)
|
||||||
msg = _('Google Authenticator code:')
|
msg = _('Google Authenticator code:')
|
||||||
|
@ -268,18 +266,20 @@ class Plugin(TrustedCoinPlugin):
|
||||||
hbox.addWidget(pw)
|
hbox.addWidget(pw)
|
||||||
vbox.addLayout(hbox)
|
vbox.addLayout(hbox)
|
||||||
|
|
||||||
def set_enabled():
|
cb_lost = QCheckBox(_("I have lost my Google Authenticator account"))
|
||||||
window.next_button.setEnabled(len(pw.text()) == 6)
|
cb_lost.setToolTip(_("Check this box to request a new secret. You will need to retype your seed."))
|
||||||
pw.textChanged.connect(set_enabled)
|
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('')
|
|
||||||
|
|
|
@ -74,9 +74,9 @@ class TrustedCoinException(Exception):
|
||||||
self.status_code = status_code
|
self.status_code = status_code
|
||||||
|
|
||||||
class TrustedCoinCosignerClient(object):
|
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.base_url = base_url
|
||||||
self.debug = debug
|
self.debug = False
|
||||||
self.user_agent = user_agent
|
self.user_agent = user_agent
|
||||||
|
|
||||||
def send_request(self, method, relative_url, data=None):
|
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)
|
return self.send_request('post', 'cosigner/%s/auth' % quote(id), payload)
|
||||||
|
|
||||||
def get(self, id):
|
def get(self, id):
|
||||||
"""
|
""" Get billing info """
|
||||||
Attempt to authenticate for a particular cosigner.
|
|
||||||
:param id: the id of the cosigner
|
|
||||||
:param otp: the one time password
|
|
||||||
"""
|
|
||||||
return self.send_request('get', 'cosigner/%s' % quote(id))
|
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):
|
def sign(self, id, transaction, otp):
|
||||||
"""
|
"""
|
||||||
Attempt to authenticate for a particular cosigner.
|
Attempt to authenticate for a particular cosigner.
|
||||||
|
@ -478,8 +483,27 @@ class TrustedCoinPlugin(BasePlugin):
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
wizard.show_message(str(e))
|
wizard.show_message(str(e))
|
||||||
return
|
return
|
||||||
if not self.setup_google_auth(wizard, short_id, otp_secret):
|
self.check_otp(wizard, short_id, otp_secret, xpub3)
|
||||||
wizard.show_message("otp error")
|
|
||||||
|
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
|
return
|
||||||
k3 = keystore.from_xpub(xpub3)
|
k3 = keystore.from_xpub(xpub3)
|
||||||
wizard.storage.put('x3/', k3.dump())
|
wizard.storage.put('x3/', k3.dump())
|
||||||
|
@ -488,6 +512,34 @@ class TrustedCoinPlugin(BasePlugin):
|
||||||
wizard.wallet = Wallet_2fa(wizard.storage)
|
wizard.wallet = Wallet_2fa(wizard.storage)
|
||||||
wizard.run('create_addresses')
|
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
|
@hook
|
||||||
def get_action(self, storage):
|
def get_action(self, storage):
|
||||||
if storage.get('wallet_type') != '2fa':
|
if storage.get('wallet_type') != '2fa':
|
||||||
|
|
Loading…
Reference in New Issue