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)
|
||||
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('')
|
||||
|
|
|
@ -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':
|
||||
|
|
Loading…
Reference in New Issue