save progress

This commit is contained in:
Istvan Ruzman
2020-08-16 16:35:13 +02:00
parent 6023ec948a
commit 10766b842c
3 changed files with 177 additions and 24 deletions

View File

@@ -4,7 +4,7 @@
"""Collection of functions to deal with RADIUS packet en- and decoding."""
from collections import namedtuple
from typing import List, Tuple, Union
from typing import List, Optional, Tuple, Union
import hashlib
import secrets
@@ -76,7 +76,7 @@ def parse_attributes(
except (PacketError, IndexError):
attributes.append(
Attribute(
name="Unknown-Vendor-Attribute",
name="Unknown-Attribute",
pos=offset,
type="octets",
length=int(packet[1]),
@@ -100,7 +100,6 @@ def parse_vendor_attributes(
vendor_id = int.from_bytes(vendor_value[:4], "big")
vendor_dict = rad_dict.vendor[vendor_id]
vendor_prefix = [26, vendor_id]
vendor_name = vendor_dict.name
attributes = []
vendor_tlv = vendor_value[4:]
@@ -110,7 +109,7 @@ def parse_vendor_attributes(
except struct.error:
attribute = [
Attribute(
name=f"Unknown-{vendor_name}-Attribute",
name="Unknown-Attribute",
pos=offset - len(vendor_value),
type="octets",
length=len(vendor_value) - 4,
@@ -176,20 +175,20 @@ def password_encode(
"""Obfuscate the plaintext Password for RADIUS"""
buf = password + b"\x00" * (16 - (len(password) % 16))
last = authenticator
results = []
results = b""
while buf:
cur_hash = MD5(secret + last).digest()
tmp = [cbuf ^ chash for cbuf, chash in zip(buf, cur_hash)]
tmp = bytes([cbuf ^ chash for cbuf, chash in zip(buf, cur_hash)])
results += tmp
(last, buf) = (bytes(tmp), buf[16:])
(last, buf) = (tmp, buf[16:])
return bytes(results)
return results
def password_decode(
secret: bytes, authenticator: bytes, obfuscated_password: bytes
) -> str:
) -> bytes:
"""Reverse the RADIUS obfuscation on a given password
The password password is padded with \\x00 to a 16 byte boundary. The padding will
@@ -206,7 +205,7 @@ def password_decode(
results += [cbuf ^ chash for cbuf, chash in zip(buf, cur_hash)]
(last, buf) = (buf[:16], buf[16:])
return bytes(results).rstrip(b"\x00").decode("utf-8")
return bytes(results).rstrip(b"\x00")
def create_chap_password(
@@ -238,20 +237,29 @@ def validate_chap_password(
)
def salt_encrypt(secret: bytes, authenticator: bytes, value: bytes) -> bytes:
def salt_encrypt(
secret: bytes,
authenticator: bytes,
value: bytes,
salt: Optional[int] = None,
) -> bytes:
"""Salt Encrypt the given value"""
# The highest bit MUST be 1
random_value = RANDOM_GENERATOR.randrange(32768, 65535)
salt = struct.pack("!H", random_value)
if salt is None:
# The highest bit MUST be 1
salt = RANDOM_GENERATOR.randrange(32768, 65535)
salted_auth = authenticator + salt
bsalt = salt.to_bytes(2, "big")
salted_auth = authenticator + bsalt
prepared_value = len(value).to_bytes(1, "big") + value
return password_encode(secret, salted_auth, value)
return bsalt + password_encode(secret, salted_auth, prepared_value)
def salt_decrypt(
secret: bytes, authenticator: bytes, salt: bytes, encrypted_value: bytes
) -> str:
secret: bytes, authenticator: bytes, encrypted_value: bytes, salt: int
) -> bytes:
"""Decrypt the given value"""
salted_auth = authenticator + salt
return password_decode(secret, salted_auth, encrypted_value)
salted_auth = authenticator + salt.to_bytes(2, "big")
decoded = password_decode(secret, salted_auth, encrypted_value)
length = decoded[0] + 1
return decoded[1:length]