sshwot/src/hashing.py

68 lines
1.9 KiB
Python

import base64
import hashlib
import os
def hash_with_salt(host, salt):
"""hash_with_salt(bytes, bytes) → bytes[32]
Hash the host using sha256 and the give salt"""
assert type(host) == bytes
assert type(salt) == bytes
m = hashlib.sha256()
m.update(host)
m.update(salt)
return m.digest()
def generate_salt():
"""generate_salt() → bytes[32]
Generates 32 bytes of randomness using the system urandom"""
return os.urandom(32)
def hash_host(host):
"""hash_host(bytes) → (bytes[32]: salt, bytes[32]: hashed_host)
Generates a salt and hashes the host with it"""
assert type(host) == bytes
salt = generate_salt()
hashed_host = hash_with_salt(host, salt)
return salt, hashed_host
def base64enc(b):
"""base64enc(bytes) → bytes
Uses no padding"""
# Base 64 encodes 3 bytes as 4 characters
# /byte 1\/byte 2\/byte 3\
# ABCDEFGHijklmnopQRSTUVWX
# \64 1/\64 2/\64 3/\64 4/
#
# If you have only one or two bytes, you don't have enough bits to
# fill all of the characters. The rest of the bits will be taken to
# be zeroes.
# /byte 1\
# ABCDEFGH0000
# \64 1/\64 2/
# /byte 1\
#
# /byte 1\/byte 2\
# ABCDEFGHijklmnop00
# \64 1/\64 2/\64 3/
#
# This way you end up with only 2 or 3 characters containing info.
# This usually gets padded into a multiple of 4 with =. However,
# since the amount of bytes left over mod 4 is enough to generate
# the padding back, we can strip it out
return base64.b64encode(b).replace(b'=', b'')
def base64dec(b64):
"""base64dec(bytes) → bytes
Can handle lack of padding."""
assert type(b64) == bytes
# Padded base64 is always a multiple of 4 bytes in length. The
# reasoning for this is because base64 decoding operates in groups
# of 4 base64 characters.
# Since we know the length of the string minus the padding, we can
# just pad it to the nearest multiple of 4
missing_padding_len = (4 - len(b64)%4) % 4
padding = b'=' * missing_padding_len
return base64.b64decode(b64 + padding, validate = True)