|
3 | 3 |
|
4 | 4 | The Message Digest algorithms were designed by Ronald Rivest: MD2 (1989, implemented here as it is not provided by |
5 | 5 | hashlib anymore), MD4 (1990, only when available in hashlib) and MD5 (1991), all producing 128-bit digests ; they are |
6 | | - now considered broken for cryptographic use. These codecs return the hexadecimal digest (one-way), for use with other |
7 | | - codecs in encoding chains. |
| 6 | + now considered broken for cryptographic use. MD6 (2008, submitted to the SHA-3 competition) is unrelated to them: it |
| 7 | + compresses blocks of 512 bytes into chaining values of 128 bytes along a 4-ary tree, which the last compression |
| 8 | + truncates to the digest size (from 8 to 512 bits, given with the codec name, e.g. "md6-512" ; it defaults to 256). It |
| 9 | + is implemented here in its default mode, unkeyed and with a tree height of 64. These codecs return the hexadecimal |
| 10 | + digest (one-way), for use with other codecs in encoding chains. |
8 | 11 |
|
9 | 12 | These codecs: |
10 | 13 | - transform strings from str to str |
|
14 | 17 | Reference: https://datatracker.ietf.org/doc/html/rfc1319 |
15 | 18 | https://datatracker.ietf.org/doc/html/rfc1320 |
16 | 19 | https://datatracker.ietf.org/doc/html/rfc1321 |
| 20 | + https://people.csail.mit.edu/rivest/pubs/RABCx08.pdf |
17 | 21 | """ |
| 22 | +from math import isqrt |
| 23 | + |
18 | 24 | from ..__common__ import * |
19 | 25 |
|
20 | 26 |
|
@@ -59,8 +65,59 @@ def md2(data): |
59 | 65 | return "".join("{:02x}".format(x) for x in digest[:16]) |
60 | 66 |
|
61 | 67 |
|
| 68 | +# MD6 constants (see the specification, section 9): Q (15 words of the fractional part of sqrt(6)), the tap positions, |
| 69 | +# the shift amounts and the round constants (initial value and mask) |
| 70 | +M64 = (1 << 64) - 1 |
| 71 | +MD6_Q = [isqrt(6 << 1920) >> (64 * (14 - i)) & M64 for i in range(15)] |
| 72 | +MD6_T = (17, 18, 21, 31, 67, 89) |
| 73 | +MD6_R = (10, 5, 13, 10, 11, 12, 2, 7, 14, 15, 7, 13, 11, 7, 6, 12) |
| 74 | +MD6_L = (11, 24, 9, 16, 15, 9, 27, 15, 6, 2, 29, 8, 15, 5, 31, 9) |
| 75 | +MD6_S0, MD6_SM = 0x0123456789abcdef, 0x7311c2812425cfa0 |
| 76 | + |
| 77 | + |
| 78 | +def __md6_compress(n, rounds): |
| 79 | + a, s, (t0, t1, t2, t3, t4, t5) = n[:], MD6_S0, MD6_T |
| 80 | + for i in range(89, 89 + 16 * rounds): |
| 81 | + j = (i - 89) % 16 |
| 82 | + if j == 0 and i > 89: |
| 83 | + s = (s << 1 | s >> 63) & M64 ^ s & MD6_SM |
| 84 | + x = s ^ a[i-t5] ^ a[i-t0] ^ (a[i-t1] & a[i-t2]) ^ (a[i-t3] & a[i-t4]) |
| 85 | + x ^= x >> MD6_R[j] |
| 86 | + a.append((x ^ x << MD6_L[j]) & M64) |
| 87 | + return a[-16:] |
| 88 | + |
| 89 | + |
| 90 | +def md6(n=""): |
| 91 | + # a bad digest size is only reported when coding, as the lookup of the bare codec name must succeed (e.g. when |
| 92 | + # listing the encodings) |
| 93 | + n = n or 256 |
| 94 | + def _hash(data, errors="strict"): |
| 95 | + if n not in range(8, 513, 8): |
| 96 | + raise LookupError("Bad parameter for encoding 'md6': digest size must be a multiple of 8 from 8 to 512") |
| 97 | + # default mode (see the specification, section 2.4): no key, tree of height L=64 and r=40+d/4 rounds ; with |
| 98 | + # L=64, the sequential mode at level L+1 is never reached |
| 99 | + m, rounds, lvl, key = b(data), 40 + n // 4, 0, [0] * 8 |
| 100 | + while True: |
| 101 | + lvl += 1 |
| 102 | + nb, out = max(1, -(-len(m) // 512)), b"" |
| 103 | + for i in range(nb): |
| 104 | + blk = m[512*i:512*(i+1)] |
| 105 | + # control word: rounds, L, final compression flag (z), padding bits (p), key length and digest size |
| 106 | + p = 8 * (512 - len(blk)) |
| 107 | + v = rounds << 48 | 64 << 40 | (nb == 1) << 36 | p << 20 | n |
| 108 | + blk += b"\x00" * (p // 8) |
| 109 | + w = [int.from_bytes(blk[k:k+8], "big") for k in range(0, 512, 8)] |
| 110 | + out += b"".join(x.to_bytes(8, "big") for x in __md6_compress(MD6_Q + key + [lvl << 56 | i, v] + w, |
| 111 | + rounds)) |
| 112 | + m = out |
| 113 | + if nb == 1: |
| 114 | + return m[-n // 8:].hex(), len(data) |
| 115 | + return _hash |
| 116 | + |
| 117 | + |
62 | 118 | add("md2", lambda s, error="strict": (md2(s), len(s)), guess=None) |
63 | 119 | add("md5", lambda s, error="strict": (hashlib.new("md5", b(s)).hexdigest(), len(s)), guess=None) |
64 | 120 | if "md4" in hashlib.algorithms_available: |
65 | 121 | add("md4", lambda s, error="strict": (hashlib.new("md4", b(s)).hexdigest(), len(s)), guess=None) |
66 | 122 |
|
| 123 | +add("md6", md6, pattern=r"^md6(?:[-_]?(\d+))?$", guess=None) |
0 commit comments