Skip to content

Instantly share code, notes, and snippets.

@clickyotomy
Created September 1, 2016 18:47
Show Gist options
  • Save clickyotomy/21502f51a72c6450d51edb3431675f35 to your computer and use it in GitHub Desktop.
Save clickyotomy/21502f51a72c6450d51edb3431675f35 to your computer and use it in GitHub Desktop.
Login module for Keybase
#! /usr/bin/env python2.7
'''
Login module doe Keybase.
'''
import json
import hmac
import base64
import hashlib
import getpass
import binascii
import pyscrypt
import requests
# Keybase API URLs.
KEYBASE_LOGIN = 'https://keybase.io/_/api/1.0/login.json'
KEYBASE_GET_SALT = 'https://keybase.io/_/api/1.0/getsalt.json'
def http_debug(response):
'''
Print the HTTP request/response debug log.
'''
print 'http-request\n{0}\n'.format('-' * len('http-request'))
print 'url ({0}): {1}'.format(response.request.method,
response.request.url)
print 'request-headers:'
print json.dumps(dict(response.request.headers), indent=4)
if response.request.method != 'GET':
if response.request.body is not None:
print 'request-payload:'
print json.dumps(json.loads(response.request.body), indent=4)
print '\nhttp-response\n{0}\n'.format('-' * len('http-response'))
print 'status-code: {0} {1}'.format(response.status_code, response.reason)
print 'url: {0}'.format(response.url)
print 'time-elapsed: {0}s'.format(response.elapsed.total_seconds())
print 'response-headers:'
print json.dumps(dict(response.headers), indent=4)
print 'response-content:'
print None if response.content is '' else json.dumps(response.json(),
indent=4)
def getsalt(username, debug=False):
'''
keybase-auth: First step of authentication; gets salt, login_session.
https://keybase.io/docs/api/1.0/call/getsalt
'''
params = {
'email_or_username': username.strip()
}
try:
response = requests.get(KEYBASE_GET_SALT, params=params)
if debug:
http_debug(response)
return response.json()
except requests.exceptions.RequestException as http_error:
print http_error[0][0]
except (ValueError, KeyError):
print 'Unable to parse response from Keybase.'
return None
def login(username, password, debug=True):
'''
keybase-auth: Second step of authentication; gets session, me.
https://keybase.io/docs/api/1.0/call/login
'''
required = set(['salt', 'login_session'])
salt_data, salt, login_session = getsalt(username, debug), None, None
if salt_data is not None and required.issubset(set(salt_data.keys())):
salt, login_session = salt_data['salt'], salt_data['login_session']
hashed = pyscrypt.hash(password=password, N=2**15, r=8, p=1, dkLen=224,
salt=binascii.unhexlify(salt))[192:224]
hmac_hashed = hmac.new(hashed, base64.decodestring(login_session),
hashlib.sha512).hexdigest()
params = {
'hmac_pwh': hmac_hashed,
'login_session': login_session,
'email_or_username': username.strip()
}
try:
response = requests.post(KEYBASE_LOGIN, params=params)
if debug:
http_debug(response)
print response.json()
except requests.exceptions.RequestException as http_error:
print http_error[0][0]
except (ValueError, KeyError):
print 'Unable to parse response from Keybase.'
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment