added md5 and md4

This commit is contained in:
Louis Heredero 2024-12-21 23:41:44 +01:00
parent 226a728e82
commit 02ee768ebb
Signed by: HEL
GPG Key ID: 8D83DE470F8544E7
6 changed files with 291 additions and 3 deletions

Binary file not shown.

View File

@ -13,6 +13,12 @@
) )
#tidy.show-module(sha-doc) #tidy.show-module(sha-doc)
#let md-doc = mod(
read("src/md.typ"),
name: "md"
)
#tidy.show-module(md-doc)
#let misc-doc = mod( #let misc-doc = mod(
read("src/misc.typ"), read("src/misc.typ"),
name: "misc" name: "misc"

View File

@ -1,4 +1,5 @@
#import "misc.typ": * #import "misc.typ": *
#import "sha.typ" #import "sha.typ": *
#import "md.typ": *
#import "utils.typ": * #import "utils.typ": *
#import "base.typ": * #import "base.typ": *

259
src/md.typ Normal file
View File

@ -0,0 +1,259 @@
#import "utils.typ": *
#let rots = (
7, 12, 17, 22, 7, 12, 17, 22, 7, 12, 17, 22, 7, 12, 17, 22,
5, 9, 14, 20, 5, 9, 14, 20, 5, 9, 14, 20, 5, 9, 14, 20,
4, 11, 16, 23, 4, 11, 16, 23, 4, 11, 16, 23, 4, 11, 16, 23,
6, 10, 15, 21, 6, 10, 15, 21, 6, 10, 15, 21, 6, 10, 15, 21
)
#let _md5-const(i) = {
return int(
calc.floor(
calc.abs(
calc.sin(i + 1) * max-32
)
)
)
}
#let md5-default-iv = (
0x67452301,
0xEFCDAB89,
0x98BADCFE,
0x10325476
)
#let md4-default-iv = md5-default-iv
/// Message Digest 5
/// #test(
/// `jumble.bytes-to-hex(jumble.md5("Hello World!")) == "ed076287532e86365e841e92bfc50d8c"`
/// )
/// ```example
/// #bytes-to-hex(md5("Hello World!"))
/// ```
/// -> bytes
#let md5(
message,
iv: md5-default-iv
) = {
let message = if type(message) == str {bytes(message)} else {message}
assert(type(message) == bytes, message: "message must be a string or bytes, but is " + repr(type(message)))
// Complete message to multiple of 512 bits
let bin-str = ""
for char in message {
bin-str += z-fill(str(char, base: 2), 8)
}
let l = bin-str.len()
bin-str += "1"
let padding = calc.rem-euclid(448 - bin-str.len(), 512)
if padding != 0 {
bin-str += "0" * padding
}
let len-bin = z-fill(str(l, base: 2), 64)
// Reverse order to little-endian
len-bin = len-bin.clusters()
.chunks(8)
.rev()
.flatten()
.join()
bin-str += len-bin
let bin = bin-str.clusters().map(int)
// Split into blocks of 16 32-bit words
let words = bin.chunks(32).map(bin-to-int).map(switch-endianness)
let blocks = words.chunks(16)
let vec = iv
for block in blocks {
let (A, B, C, D) = vec
for i in range(64) {
let (f, g) = if i < 16 {(
B.bit-and(C).bit-or(B.bit-not().bit-and(mask-32).bit-and(D)),
i
)} else if i < 32 {(
D.bit-and(B).bit-or(D.bit-not().bit-and(mask-32).bit-and(C)),
calc.rem(5 * i + 1, 16)
)} else if i < 48 {(
B.bit-xor(C).bit-xor(D),
calc.rem(3 * i + 5, 16)
)} else {(
C.bit-xor(B.bit-or(D.bit-not().bit-and(mask-32))),
calc.rem(7 * i, 16)
)}
let temp = circular-shift(
calc.rem(A + f + _md5-const(i) + block.at(g), max-32),
n: rots.at(i)
) + B
(A, B, C, D) = (
D,
calc.rem(temp, max-32),
B,
C
)
}
vec = vec.zip((A, B, C, D)).map(p => {
calc.rem(p.sum(), max-32)
})
}
let digest-bytes = ()
for n in vec {
digest-bytes += (
n.bit-and(0xff),
n.bit-rshift(8).bit-and(0xff),
n.bit-rshift(16).bit-and(0xff),
n.bit-rshift(24)
)
}
return bytes(digest-bytes)
}
#let _md4-round1-shift = (3, 7, 11, 19)
#let _md4-round2-shift = (3, 5, 9, 13)
#let _md4-round3-shift = (3, 9, 11, 15)
#let _md4-f(x, y, z) = {
return x.bit-and(y).bit-or(x.bit-not().bit-and(mask-32).bit-and(z))
}
#let _md4-g(x, y, z) = {
let xy = x.bit-and(y)
let xz = x.bit-and(z)
let yz = y.bit-and(z)
return xy.bit-or(xz).bit-or(yz)
}
#let _md4-h(x, y, z) = {
return x.bit-xor(y).bit-xor(z)
}
#let _md4-round1(a, b, c, d, x, s) = {
let temp = a + _md4-f(b, c, d) + x
return circular-shift(
temp.bit-and(mask-32),
n: s
)
}
#let _md4-round2(a, b, c, d, x, s) = {
let temp = a + _md4-g(b, c, d) + x + 0x5a827999
return circular-shift(
temp.bit-and(mask-32),
n: s
)
}
#let _md4-round3(a, b, c, d, x, s) = {
let temp = a + _md4-h(b, c, d) + x + 0x6ed9eba1
return circular-shift(
temp.bit-and(mask-32),
n: s
)
}
/// Message Digest 4
/// #test(
/// `jumble.bytes-to-hex(jumble.md4("Hello World!")) == "b2a5cc34fc21a764ae2fad94d56fadf6"`
/// )
/// ```example
/// #bytes-to-hex(md4("Hello World!"))
/// ```
/// -> bytes
#let md4(
/// -> str | bytes
message,
/// -> array
iv: md4-default-iv
) = {
let message = if type(message) == str {bytes(message)} else {message}
assert(type(message) == bytes, message: "message must be a string or bytes, but is " + repr(type(message)))
// Complete message to multiple of 512 bits
let bin-str = ""
for char in message {
bin-str += z-fill(str(char, base: 2), 8)
}
let l = bin-str.len()
bin-str += "1"
let padding = calc.rem-euclid(448 - bin-str.len(), 512)
if padding != 0 {
bin-str += "0" * padding
}
let len-bin = z-fill(str(l, base: 2), 64)
// Reverse order to little-endian
len-bin = len-bin.clusters()
.chunks(8)
.rev()
.flatten()
.join()
bin-str += len-bin
let bin = bin-str.clusters().map(int)
// Split into blocks of 16 32-bit words
let words = bin.chunks(32).map(bin-to-int).map(switch-endianness)
let blocks = words.chunks(16)
let vec = iv
for block in blocks {
let vec2 = vec
for i in range(16) {
vec2.at(0) = _md4-round1(
..vec2,
block.at(i),
_md4-round1-shift.at(calc.rem(i, 4))
)
vec2.insert(0, vec2.pop())
}
for i in range(16) {
let j = calc.rem(i * 4, 16) + calc.div-euclid(i, 4)
vec2.at(0) = _md4-round2(
..vec2,
block.at(j),
_md4-round2-shift.at(calc.rem(i, 4))
)
vec2.insert(0, vec2.pop())
}
for i in range(16) {
let j = bin-to-int(
z-fill(
str(i, base: 2),
4
).clusters()
.map(int)
.rev()
)
vec2.at(0) = _md4-round3(
..vec2,
block.at(j),
_md4-round3-shift.at(calc.rem(i, 4))
)
vec2.insert(0, vec2.pop())
}
vec = vec.zip(vec2).map(p => {
calc.rem(p.sum(), max-32)
})
}
let digest-bytes = ()
for n in vec {
digest-bytes += (
n.bit-and(0xff),
n.bit-rshift(8).bit-and(0xff),
n.bit-rshift(16).bit-and(0xff),
n.bit-rshift(24),
)
}
return bytes(digest-bytes)
}

View File

@ -42,15 +42,18 @@
/// -> bytes /// -> bytes
#let sha1( #let sha1(
/// Message to hash /// Message to hash
/// -> str /// -> str | bytes
message, message,
/// Initial vector /// Initial vector
/// -> array /// -> array
iv: sha1-default-iv iv: sha1-default-iv
) = { ) = {
let message = if type(message) == str {bytes(message)} else {message}
assert(type(message) == bytes, message: "message must be a string or bytes, but is " + repr(type(message)))
// Complete message to multiple of 512 bits // Complete message to multiple of 512 bits
let bin-str = "" let bin-str = ""
for char in bytes(message) { for char in message {
bin-str += z-fill(str(char, base: 2), 8) bin-str += z-fill(str(char, base: 2), 8)
} }
let l = bin-str.len() let l = bin-str.len()

View File

@ -97,4 +97,23 @@
} }
let high-bits = x.bit-rshift(32 - n) let high-bits = x.bit-rshift(32 - n)
return x.bit-lshift(n).bit-or(high-bits).bit-and(mask-32) return x.bit-lshift(n).bit-or(high-bits).bit-and(mask-32)
}
/// Switches the endianness of the given value (32-bit integer)
/// -> number
#let switch-endianness(
/// -> number
value
) = {
let a = value.bit-rshift(24)
let b = value.bit-rshift(16).bit-and(0xff)
let c = value.bit-rshift(8).bit-and(0xff)
let d = value.bit-and(0xff)
let a2 = d.bit-lshift(24)
let b2 = c.bit-lshift(16)
let c2 = b.bit-lshift(8)
let d2 = a
return a2.bit-or(b2).bit-or(c2).bit-or(d2)
} }