Module network_hash_gen.juniper_junos
This module provides hash functions for Juniper Junos
Supported are:
- Type 1 (md5-based)
- Type 6 (sha512-based)
- Type 9 (Vigenère-based (not a secure hash, must a obfuscation))
Expand source code
"""
This module provides hash functions for Juniper Junos
Supported are:
- Type 1 (md5-based)
- Type 6 (sha512-based)
- Type 9 (Vigenère-based (not a secure hash, must a obfuscation))
"""
from .type_1 import Type1
from .type_6 import Type6
from .type_9 import Type9
__pdoc__ = {}
__all__ = ["Type1", "Type6", "Type9"]
Sub-modules
network_hash_gen.juniper_junos.type_1
network_hash_gen.juniper_junos.type_6
network_hash_gen.juniper_junos.type_9
Classes
class Type1
-
Calculates the md5-crypt based type 1 hashes for Juniper Junos.
Expand source code
class Type1(CiscoType5): """ Calculates the md5-crypt based type 1 hashes for Juniper Junos. """ salt_length = 8
Ancestors
Class variables
var salt_chars : str
-
Inherited from:
BaseHash
.salt_chars
A string containing all characters that can be used in a salt.
var salt_length : int
-
Inherited from:
BaseHash
.salt_length
The length of the salt.
Static methods
def hash(password: str) ‑> str
-
Calculates the hash …
def hash_salted(password: str, salt: str) ‑> str
-
Inherited from:
Type5
.hash_salted
Calculates a Cisco IOS/IOS-XE Type 5 hash with the given password and salt …
def hash_seeded(password: str, seed: str) ‑> str
-
Inherited from:
Type5
.hash_seeded
Calculates a hash with the given seed used for generating a appropriate salt …
class Type6
-
SHA2 512 bit based hash for Juniper JunOS.
Specification: https://akkadia.org/drepper/SHA-crypt.txt Details: - 5000 rounds are used - the round specifier is omitted.
Expand source code
class Type6(BaseHash): """ SHA2 512 bit based hash for Juniper JunOS. Specification: https://akkadia.org/drepper/SHA-crypt.txt Details: - 5000 rounds are used - the round specifier is omitted. """ salt_length = 8 salt_chars = "./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz" @staticmethod def hash_salted(password: str, salt: str) -> str: return sha512_crypt.using(salt=salt, rounds=5000).hash(password)
Ancestors
Class variables
var salt_chars : str
-
Inherited from:
BaseHash
.salt_chars
A string containing all characters that can be used in a salt.
var salt_length : int
-
Inherited from:
BaseHash
.salt_length
The length of the salt.
Static methods
def hash(password: str) ‑> str
-
Calculates the hash …
def hash_salted(password: str, salt: str) ‑> str
-
Inherited from:
BaseHash
.hash_salted
calculates a hash from a fiven password and salt. If the salt is invalid for the hash, the behaviour is undefined. Prefer the
hash
andhash_seeded
…Expand source code
@staticmethod def hash_salted(password: str, salt: str) -> str: return sha512_crypt.using(salt=salt, rounds=5000).hash(password)
def hash_seeded(password: str, seed: str) ‑> str
-
Inherited from:
BaseHash
.hash_seeded
Calculates a hash with the given seed used for generating a appropriate salt …
class Type9
-
The JunOS Type 9 password obfuscation algorithm.
It is used to store passwords in the configuration that need to be available in plaintext, but should not be easily read by humans. For example passwords for BGP sessions.
This is not a secure hash function!
This implementation is based on code from https://github.com/msuksong/junosdecode/blob/master/junosdecode.py
Expand source code
class Type9: """ The JunOS Type 9 password obfuscation algorithm. It is used to store passwords in the configuration that need to be available in plaintext, but should not be easily read by humans. For example passwords for BGP sessions. This is not a secure hash function! This implementation is based on code from https://github.com/msuksong/junosdecode/blob/master/junosdecode.py """ __MAGIC = "$9$" __FAMILY: List[str] = [ "QzF3n6/9CAtpu0O", "B1IREhcSyrleKvMW8LXx", "7N-dVbwsY2g4oaJZGUDj", "iHkq.mPf5T", ] # maps single characters to numbers from 0 to 3 (incl.) __EXTRA: Dict[str, int] = dict() for x, item in enumerate(__FAMILY): for c in item: __EXTRA[c] = 3 - x # List of 65 character, in sorted order they are: # -./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz __NUM_ALPHA: str = "".join(__FAMILY) # reverse mapping from __NUM_ALPHA __ALPHA_NUM: Dict[str, int] = {x: i for (i, x) in enumerate("".join(__FAMILY))} # encoding moduli by position __ENCODING = [ [1, 4, 32], [1, 16, 32], [1, 8, 32], [1, 64], [1, 32], [1, 4, 16, 128], [1, 32, 64], ] @staticmethod def encode(plaintext: str) -> str: """ Encoded the given plaintext. Generates a random salt. """ salt = Type9._generate_salt(length=1) return Type9.encode_salted(plaintext, salt) @staticmethod def encode_seeded(plaintext: str, seed: str) -> str: salt = Type9._generate_salt(length=1, seed=seed) return Type9.encode_salted(plaintext, salt) @staticmethod def encode_salted(plaintext: str, salt: str) -> str: """ Encodes the given plaintext. """ assert len(salt) == 1 assert salt in Type9.__NUM_ALPHA # an additional, random string of length 0 to 3 (incl.) rand: str = Type9._generate_salt(Type9.__EXTRA[salt], seed=salt) position: int = 0 prev: str = salt crypt = Type9.__MAGIC + salt + rand for x in plaintext: moduli = Type9.__ENCODING[position % len(Type9.__ENCODING)] crypt += Type9._gap_encode(x, prev, moduli) prev = crypt[-1] position += 1 return crypt @staticmethod def decode(secret: str) -> str: """ Decodes the given secret. """ splits: List[str] = secret.split("$") assert len(splits) == 3 magic: str = splits[1] assert magic == "9" encoding: str = splits[2] salt: str = encoding[0] rand_length: int = Type9.__EXTRA[salt] crypt: str = encoding[1 + rand_length :] input_position: int = 0 prev: str = salt output: str = "" while input_position < len(crypt): moduli: List[int] = Type9.__ENCODING[len(output) % len(Type9.__ENCODING)] assert input_position + len(moduli) <= len(crypt) # the 2 to 4 characters that define the next output character enc: str = crypt[input_position : input_position + len(moduli)] gaps: List[int] = list() for character in enc: gap = Type9.__ALPHA_NUM[character] - Type9.__ALPHA_NUM[prev] - 1 gap %= len(Type9.__NUM_ALPHA) gaps.append(gap) prev = character result = 0 for i, gap in enumerate(gaps): result += gap * moduli[i] output += chr(result) input_position += len(moduli) return output @staticmethod def _gap_encode(pc: str, prev: str, moduli: List[int]): """ pc is the plaintext byte, prev is the previous encoding, moduli is a list of moduli. """ assert len(pc) == 1 assert len(prev) == 1 _ord = ord(pc) # Unicode value of that character crypt: str = "" gaps: List = [] # builds a encoding based on the given module. # with moduli [1, 2, 4, 8, ...] this would be binary encoding. # with moduli [1, 16, 256] it would be hexadecimal # but in this case each position can have a different value for mod in reversed(moduli): gaps.insert(0, int(_ord / mod)) _ord %= mod for gap in gaps: gap += Type9.__ALPHA_NUM[prev] + 1 prev = Type9.__NUM_ALPHA[gap % len(Type9.__NUM_ALPHA)] crypt += prev assert len(crypt) == len(moduli) return crypt @staticmethod def _parse_encoding(secret: str) -> Tuple[str, str, str]: """ Returns a tuple containing the salt, rand and the encoded part of the given secret """ splits: List[str] = secret.split("$") assert len(splits) == 3 magic: str = splits[1] assert magic == "9" encoding: str = splits[2] salt: str = encoding[0] rand_length: int = Type9.__EXTRA[salt] rand: str = encoding[1 : 1 + rand_length] crypt: str = encoding[1 + rand_length :] return salt, rand, crypt @staticmethod def _generate_salt(length: int, seed: Optional[str] = None) -> str: return _generate_salt("".join(Type9.__NUM_ALPHA), length, seed)
Class variables
var c
var item
var x
Static methods
def decode(secret: str) ‑> str
-
Decodes the given secret.
Expand source code
@staticmethod def decode(secret: str) -> str: """ Decodes the given secret. """ splits: List[str] = secret.split("$") assert len(splits) == 3 magic: str = splits[1] assert magic == "9" encoding: str = splits[2] salt: str = encoding[0] rand_length: int = Type9.__EXTRA[salt] crypt: str = encoding[1 + rand_length :] input_position: int = 0 prev: str = salt output: str = "" while input_position < len(crypt): moduli: List[int] = Type9.__ENCODING[len(output) % len(Type9.__ENCODING)] assert input_position + len(moduli) <= len(crypt) # the 2 to 4 characters that define the next output character enc: str = crypt[input_position : input_position + len(moduli)] gaps: List[int] = list() for character in enc: gap = Type9.__ALPHA_NUM[character] - Type9.__ALPHA_NUM[prev] - 1 gap %= len(Type9.__NUM_ALPHA) gaps.append(gap) prev = character result = 0 for i, gap in enumerate(gaps): result += gap * moduli[i] output += chr(result) input_position += len(moduli) return output
def encode(plaintext: str) ‑> str
-
Encoded the given plaintext. Generates a random salt.
Expand source code
@staticmethod def encode(plaintext: str) -> str: """ Encoded the given plaintext. Generates a random salt. """ salt = Type9._generate_salt(length=1) return Type9.encode_salted(plaintext, salt)
def encode_salted(plaintext: str, salt: str) ‑> str
-
Encodes the given plaintext.
Expand source code
@staticmethod def encode_salted(plaintext: str, salt: str) -> str: """ Encodes the given plaintext. """ assert len(salt) == 1 assert salt in Type9.__NUM_ALPHA # an additional, random string of length 0 to 3 (incl.) rand: str = Type9._generate_salt(Type9.__EXTRA[salt], seed=salt) position: int = 0 prev: str = salt crypt = Type9.__MAGIC + salt + rand for x in plaintext: moduli = Type9.__ENCODING[position % len(Type9.__ENCODING)] crypt += Type9._gap_encode(x, prev, moduli) prev = crypt[-1] position += 1 return crypt
def encode_seeded(plaintext: str, seed: str) ‑> str
-
Expand source code
@staticmethod def encode_seeded(plaintext: str, seed: str) -> str: salt = Type9._generate_salt(length=1, seed=seed) return Type9.encode_salted(plaintext, salt)