Move sasl.py to a directory module and move SCRAM logic to a different file,
move `github/module.py` to `github/__init__.py`
This commit is contained in:
parent
c311521a31
commit
1fe20a2c98
3 changed files with 98 additions and 55 deletions
|
@ -1,5 +1,6 @@
|
||||||
import base64, hashlib, hmac, uuid
|
import base64, hashlib, hmac, uuid
|
||||||
from src import ModuleManager, utils
|
from src import ModuleManager, utils
|
||||||
|
from . import scram
|
||||||
|
|
||||||
def _validate(self, s):
|
def _validate(self, s):
|
||||||
mechanism = s
|
mechanism = s
|
||||||
|
@ -68,62 +69,17 @@ class Module(ModuleManager.BaseModule):
|
||||||
sasl_username, sasl_password = sasl["args"].split(":", 1)
|
sasl_username, sasl_password = sasl["args"].split(":", 1)
|
||||||
if event["message"] == "+":
|
if event["message"] == "+":
|
||||||
# start SCRAM handshake
|
# start SCRAM handshake
|
||||||
first_base = "n=%s,r=%s" % (
|
event["server"]._scram = scram.SCRAM(
|
||||||
_scram_escape(sasl_username), _scram_nonce())
|
algo, sasl_username, sasl_password)
|
||||||
first_withchannel = "n,,%s" % first_base
|
auth_text = event["server"]._scram.client_first()
|
||||||
auth_text = first_withchannel.encode("utf8")
|
print(auth_text)
|
||||||
event["server"]._scram_first = first_base.encode("utf8")
|
|
||||||
else:
|
else:
|
||||||
data = base64.b64decode(event["message"]).decode("utf8")
|
current_scram = event["server"]._scram
|
||||||
pieces = dict(piece.split("=", 1) for piece in data.split(","))
|
if current_scram.state == scram.SCRAMState.ClientFirst:
|
||||||
if "s" in pieces:
|
auth_text = current_scram.server_first(event["message"])
|
||||||
# server-first-message
|
elif current_scram.state == scram.SCRAMState.ClientFinal:
|
||||||
nonce = pieces["r"].encode("utf8")
|
auth_text = current_scram.server_final(event["message"])
|
||||||
salt = base64.b64decode(pieces["s"])
|
del event["server"]._scram
|
||||||
iterations = pieces["i"]
|
|
||||||
password = sasl_password.encode("utf8")
|
|
||||||
|
|
||||||
salted_password = hashlib.pbkdf2_hmac(algo, password, salt,
|
|
||||||
int(iterations), dklen=None)
|
|
||||||
event["server"]._scram_salted_password = salted_password
|
|
||||||
|
|
||||||
client_key = hmac.digest(salted_password, b"Client Key",
|
|
||||||
algo)
|
|
||||||
stored_key = hashlib.new(algo, client_key).digest()
|
|
||||||
|
|
||||||
channel = base64.b64encode(b"n,,")
|
|
||||||
auth_noproof = b"c=%s,r=%s" % (channel, nonce)
|
|
||||||
auth_message = b"%s,%s,%s" % (event["server"]._scram_first,
|
|
||||||
data.encode("utf8"), auth_noproof)
|
|
||||||
event["server"]._scram_auth_message = auth_message
|
|
||||||
|
|
||||||
client_signature = hmac.digest(stored_key, auth_message,
|
|
||||||
algo)
|
|
||||||
client_proof = base64.b64encode(
|
|
||||||
_scram_xor(client_key, client_signature))
|
|
||||||
|
|
||||||
auth_text = auth_noproof + (b",p=%s" % client_proof)
|
|
||||||
elif "v" in pieces:
|
|
||||||
# server-final-message
|
|
||||||
verifier = pieces["v"]
|
|
||||||
|
|
||||||
salted_password = event["server"]._scram_salted_password
|
|
||||||
auth_message = event["server"]._scram_auth_message
|
|
||||||
server_key = hmac.digest(salted_password, b"Server Key",
|
|
||||||
algo)
|
|
||||||
server_signature = hmac.digest(server_key, auth_message,
|
|
||||||
algo)
|
|
||||||
|
|
||||||
del event["server"]._scram_first
|
|
||||||
del event["server"]._scram_salted_password
|
|
||||||
del event["server"]._scram_auth_message
|
|
||||||
|
|
||||||
if server_signature != base64.b64decode(verifier):
|
|
||||||
raise ValueError("SCRAM %s authentication failed "
|
|
||||||
% algo)
|
|
||||||
event["server"].disconnect()
|
|
||||||
auth_text = "+"
|
|
||||||
|
|
||||||
else:
|
else:
|
||||||
raise ValueError("unknown sasl mechanism '%s'" % mechanism)
|
raise ValueError("unknown sasl mechanism '%s'" % mechanism)
|
||||||
|
|
87
modules/sasl/scram.py
Normal file
87
modules/sasl/scram.py
Normal file
|
@ -0,0 +1,87 @@
|
||||||
|
import base64, enum, hashlib, hmac, uuid
|
||||||
|
|
||||||
|
def _scram_nonce():
|
||||||
|
return uuid.uuid4().hex.encode("utf8")
|
||||||
|
def _scram_escape(s):
|
||||||
|
return s.replace(b"=", b"=3D").replace(b",", b"=2C")
|
||||||
|
def _scram_unescape(s):
|
||||||
|
return s.replace(b"=3D", b"=").replace(b"=2C", b",")
|
||||||
|
def _scram_xor(s1, s2):
|
||||||
|
return bytes(a ^ b for a, b in zip(s1, s2))
|
||||||
|
|
||||||
|
class SCRAMState(enum.Enum):
|
||||||
|
Uninitialised = 0
|
||||||
|
ClientFirst = 1
|
||||||
|
ClientFinal = 2
|
||||||
|
Success = 3
|
||||||
|
VerifyFailed = 4
|
||||||
|
|
||||||
|
class SCRAMError(Exception):
|
||||||
|
pass
|
||||||
|
|
||||||
|
class SCRAM(object):
|
||||||
|
def __init__(self, algo, username, password):
|
||||||
|
self._algo = algo
|
||||||
|
self._username = username.encode("utf8")
|
||||||
|
self._password = password.encode("utf8")
|
||||||
|
|
||||||
|
self.state = SCRAMState.Uninitialised
|
||||||
|
self._client_first = None
|
||||||
|
self._salted_password = None
|
||||||
|
self._auth_message = None
|
||||||
|
|
||||||
|
def _get_data(self, message):
|
||||||
|
data = base64.b64decode(message)
|
||||||
|
return data, dict(piece.split(b"=", 1) for piece in data.split(b","))
|
||||||
|
|
||||||
|
def client_first(self):
|
||||||
|
self.state = SCRAMState.ClientFirst
|
||||||
|
# start SCRAM handshake
|
||||||
|
self._client_first = b"n=%s,r=%s" % (
|
||||||
|
_scram_escape(self._username), _scram_nonce())
|
||||||
|
return b"n,,%s" % self._client_first
|
||||||
|
|
||||||
|
def server_first(self, message):
|
||||||
|
self.state = SCRAMState.ClientFinal
|
||||||
|
|
||||||
|
data, pieces = self._get_data(message)
|
||||||
|
# server-first-message
|
||||||
|
nonce = pieces[b"r"]
|
||||||
|
salt = base64.b64decode(pieces[b"s"])
|
||||||
|
iterations = pieces[b"i"]
|
||||||
|
password = self._password
|
||||||
|
|
||||||
|
salted_password = hashlib.pbkdf2_hmac(self._algo, password, salt,
|
||||||
|
int(iterations), dklen=None)
|
||||||
|
self._salted_password = salted_password
|
||||||
|
|
||||||
|
client_key = hmac.digest(salted_password, b"Client Key", self._algo)
|
||||||
|
stored_key = hashlib.new(self._algo, client_key).digest()
|
||||||
|
|
||||||
|
channel = base64.b64encode(b"n,,")
|
||||||
|
auth_noproof = b"c=%s,r=%s" % (channel, nonce)
|
||||||
|
auth_message = b"%s,%s,%s" % (self._client_first, data, auth_noproof)
|
||||||
|
self._auth_message = auth_message
|
||||||
|
|
||||||
|
client_signature = hmac.digest(stored_key, auth_message, self._algo)
|
||||||
|
client_proof = base64.b64encode(
|
||||||
|
_scram_xor(client_key, client_signature))
|
||||||
|
|
||||||
|
return auth_noproof + (b",p=%s" % client_proof)
|
||||||
|
|
||||||
|
def server_final(self, message):
|
||||||
|
# server-final-message
|
||||||
|
data, pieces = self._get_data(message)
|
||||||
|
verifier = pieces[b"v"]
|
||||||
|
|
||||||
|
server_key = hmac.digest(self._salted_password, b"Server Key",
|
||||||
|
self._algo)
|
||||||
|
server_signature = hmac.digest(server_key, self._auth_message,
|
||||||
|
self._algo)
|
||||||
|
|
||||||
|
if server_signature != base64.b64decode(verifier):
|
||||||
|
self.state = SCRAMState.VerifyFailed
|
||||||
|
return None
|
||||||
|
else:
|
||||||
|
self.state = SCRAMState.Success
|
||||||
|
return "+"
|
Loading…
Reference in a new issue