This repository has been archived by the owner on Nov 16, 2023. It is now read-only.
forked from ashleywaite/django-more
-
Notifications
You must be signed in to change notification settings - Fork 0
/
hashing.py
136 lines (105 loc) · 3.57 KB
/
hashing.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
from base64 import b64encode, b16encode, b64decode, b16decode
from math import ceil
__all__ = [
"b64max",
"b64len",
"b64from16",
"b64from256",
"b16len",
"b16max",
"b16from64",
"b16from256",
"HashString",
]
# Base 64 helpers for working in strings
# b64 encodes 6 bits per character, in 3 byte raw increments, four bytes b64
def b64max(char_length):
""" Maximum number of raw bits that can be stored in a b64 of length x """
# Four byte increments only, discard extra length
return (char_length // 4) * (3 * 8)
def b64len(bit_length):
""" Minimum b64 length required to hold x raw bits """
# Three raw bytes {bl / (8*3)} is four b64 bytes
return ceil(int(bit_length) / (8 * 3)) * 4
def b64from16(val):
""" ASCII encoded base 64 string from a base 16 digest """
return b64from256(b16decode(val, casefold=True))
def b64from256(val):
""" ASCII encoded base 64 string from a raw (base 256) digest """
return str(b64encode(bytes(val)), encoding="ascii")
# Base 16 helpers for working in strings
# b16 encodes 4 bits per character
def b16len(bit_length):
""" Minimum b16/hex length required to hold x raw bits """
return ceil(int(bit_length) / 4)
def b16max(char_length):
""" Maximum number of raw bits that can be stored in a b16 of length x """
return int(char_length) * 4
def b16from64(val):
""" ASCII encoded base 16 string from a base 64 digest """
return b16from256(b64decode(val))
def b16from256(val):
""" ASCII encoded base 16 string from a raw (base 256) digest """
return str(b16encode(bytes(val)), encoding="ascii")
class HashString(str):
b64to = {
"b16": b16from64,
"b64": str.__str__,
"b256": b64decode,
}
b16to = {
"b64": b64from16,
"b16": str.__str__,
"b256": b16decode,
}
def __new__(cls, value):
return super().__new__(cls, value)
def __getattr__(self, attr):
try:
setattr(self, attr, self.b_to[attr](self))
return getattr(self, attr)
except KeyError:
raise AttributeError
@classmethod
def from_b64(cls, value):
""" Create from a base 64 value """
self = cls(value)
self.b_to = self.b64to
return self
@classmethod
def from_b16(cls, value):
""" Create from a base 16 value """
self = cls(value)
self.b_to = self.b16to
return self
@classmethod
def from_b256(cls, value):
""" Create from a raw (base 256) value """
self = cls.from_b64(b64from256(value))
self.b256 = value
return self
def __eq__(self, value):
if isinstance(value, str):
if str.__eq__(self, value):
return True
if str.__eq__(self.b64, value):
# Check for encoding sensitive matches
return True
if str.__eq__(str(self), str.lower(value)):
# Check for lower case matches of base 16
return True
elif isinstance(value, bytes) and bytes.__eq__(self.b256, value):
return True
return False
def __bytes__(self):
# Bytes will give the base256 / raw bytes
return self.b256
def __str__(self):
# Stringy informal representations of hashes are base16 lowercase
return str.lower(self.b16)
def __repr__(self):
# Formal accurate representations of hases are base64
return self.b64
def __hash__(self):
# Hashing always uses base64 for consistency
return hash(self.b64)