initial commit (b32, sha1, hmac)
This commit is contained in:
		
							
								
								
									
										
											BIN
										
									
								
								manual.pdf
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								manual.pdf
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										34
									
								
								manual.typ
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										34
									
								
								manual.typ
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,34 @@ | ||||
| #import "@preview/tidy:0.4.0" | ||||
|  | ||||
| #import "src/lib.typ" | ||||
|  | ||||
| #let mod = tidy.parse-module.with( | ||||
|   scope: (jumble: lib), | ||||
|   preamble: "#import jumble: *;" | ||||
| ) | ||||
|  | ||||
| #let sha-doc = mod( | ||||
|   read("src/sha.typ"), | ||||
|   name: "sha" | ||||
| ) | ||||
| #tidy.show-module(sha-doc) | ||||
|  | ||||
| #let misc-doc = mod( | ||||
|   read("src/misc.typ"), | ||||
|   name: "misc" | ||||
| ) | ||||
| #tidy.show-module(misc-doc) | ||||
|  | ||||
| #let base-doc = mod( | ||||
|   read("src/base.typ"), | ||||
|   name: "base" | ||||
| ) | ||||
| #tidy.show-module(base-doc) | ||||
|  | ||||
| #pagebreak() | ||||
|  | ||||
| #let utils-doc = mod( | ||||
|   read("src/utils.typ"), | ||||
|   name: "utils" | ||||
| ) | ||||
| #tidy.show-module(utils-doc) | ||||
							
								
								
									
										62
									
								
								src/base.typ
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										62
									
								
								src/base.typ
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,62 @@ | ||||
| #import "utils.typ": z-fill, bin-to-int | ||||
| #let b32-alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567" | ||||
|  | ||||
| /// Decodes a base32-encoded value | ||||
| /// #test( | ||||
| ///   `jumble.b32-decode("LFHVKUCJ") == bytes("YOUPI")` | ||||
| /// ) | ||||
| /// ```example | ||||
| /// #str(b32-decode("LFHVKUCJ")) | ||||
| /// ``` | ||||
| /// -> bytes | ||||
| #let b32-decode( | ||||
|   /// -> str | ||||
|   encoded | ||||
| ) = { | ||||
|   let decoded-bin = () | ||||
|   for char in encoded { | ||||
|     if char == "=" { | ||||
|       break | ||||
|     } | ||||
|     let i = b32-alphabet.position(char) | ||||
|     decoded-bin += z-fill(str(i, base: 2), 5).clusters().map(int) | ||||
|   } | ||||
|   decoded-bin = decoded-bin.slice( | ||||
|     0, | ||||
|     calc.div-euclid(decoded-bin.len(), 8) * 8 | ||||
|   ) | ||||
|   let decoded-bytes = decoded-bin.chunks(8).map(bin-to-int) | ||||
|   return bytes(decoded-bytes) | ||||
| } | ||||
|  | ||||
| /// Encodes a value in base32 | ||||
| /// #test( | ||||
| ///   `jumble.b32-encode(bytes("YOUPI")) == "LFHVKUCJ"` | ||||
| /// ) | ||||
| /// ```example | ||||
| /// #b32-encode(bytes("YOUPI")) | ||||
| /// ``` | ||||
| /// -> str | ||||
| #let b32-encode( | ||||
|   /// -> bytes | ||||
|   decoded | ||||
| ) = { | ||||
|   let encoded = "" | ||||
|   let decoded-bin = array(decoded).map(b => z-fill(str(b, base: 2), 8)).join() | ||||
|   decoded-bin = decoded-bin.clusters().map(int) | ||||
|   let groups = decoded-bin.chunks(40) | ||||
|   for group in groups { | ||||
|     let chars = group.chunks(5) | ||||
|     if chars.last().len() != 5 { | ||||
|       chars.last() += (0,) * (5 - chars.last().len()) | ||||
|     } | ||||
|     let chars = chars.map(bin-to-int) | ||||
|                      .map(c => b32-alphabet.at(c)) | ||||
|     encoded += chars.join() | ||||
|     let pad = 8 - chars.len() | ||||
|     if pad != 0 { | ||||
|       encoded += "=" * pad | ||||
|     } | ||||
|   } | ||||
|   return encoded | ||||
| } | ||||
							
								
								
									
										4
									
								
								src/lib.typ
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										4
									
								
								src/lib.typ
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,4 @@ | ||||
| #import "misc.typ": * | ||||
| #import "sha.typ" | ||||
| #import "utils.typ": * | ||||
| #import "base.typ": * | ||||
							
								
								
									
										48
									
								
								src/misc.typ
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										48
									
								
								src/misc.typ
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,48 @@ | ||||
| #import "utils.typ": xor-bytes | ||||
| #import "sha.typ": sha1 | ||||
|  | ||||
| #let _compute-block-sized-key(key, hash-func: sha1, block-size: 64) = { | ||||
|   if key.len() > block-size { | ||||
|     key = hash-func(key) | ||||
|   } | ||||
|  | ||||
|   if key.len() < block-size { | ||||
|     key = bytes((0,) * (block-size - key.len())) + key | ||||
|   } | ||||
|  | ||||
|   return  key | ||||
| } | ||||
|  | ||||
| /// Hash-based Message Authentication Code | ||||
| /// ```example | ||||
| /// #bytes-to-hex(hmac("Key", "Hello World!")) | ||||
| /// ``` | ||||
| /// -> bytes | ||||
| #let hmac( | ||||
|   /// Hashing key | ||||
|   /// -> str | bytes | ||||
|   key, | ||||
|   /// Message to hash | ||||
|   /// -> str | bytes | ||||
|   message, | ||||
|   /// Hashing function | ||||
|   /// -> function | ||||
|   hash-func: sha1, | ||||
|   /// Block size | ||||
|   /// -> number | ||||
|   block-size: 64 | ||||
| ) = { | ||||
|   let key = if type(key) == str {bytes(key)} else {key} | ||||
|   let message = if type(message) == str {bytes(message)} else {message} | ||||
|   assert(type(key) == bytes, message: "key must be a string or bytes, but is " + repr(type(key))) | ||||
|   assert(type(message) == bytes, message: "message must be a string or bytes, but is " + repr(type(message))) | ||||
|  | ||||
|   let block-sized-key = _compute-block-sized-key(key, hash-func: hash-func, block-size: block-size) | ||||
|  | ||||
|   let i-pad = bytes((0x36,) * block-size) | ||||
|   let o-pad = bytes((0x5c,) * block-size) | ||||
|   let i-key-pad = xor-bytes(key, i-pad) | ||||
|   let o-key-pad = xor-bytes(key, o-pad) | ||||
|  | ||||
|   return hash-func(o-key-pad + hash-func(i-key-pad + message)) | ||||
| } | ||||
							
								
								
									
										115
									
								
								src/sha.typ
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										115
									
								
								src/sha.typ
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,115 @@ | ||||
| #import "utils.typ": * | ||||
|  | ||||
| #let sha1-default-iv = ( | ||||
|   0x67452301, | ||||
|   0xefcdab89, | ||||
|   0x98badcfe, | ||||
|   0x10325476, | ||||
|   0xc3d2e1f0 | ||||
| ) | ||||
|  | ||||
| #let _sha1-const(i) = { | ||||
|   if i < 20 { | ||||
|     return 0x5a827999 | ||||
|   } else if i < 40 { | ||||
|     return 0x6ed9eba1 | ||||
|   } else if i < 60 { | ||||
|     return 0x8f1bbcdc | ||||
|   } else { | ||||
|     return 0xca62c1d6 | ||||
|   } | ||||
| } | ||||
|  | ||||
| #let _sha1-func(i, b, c, d) = { | ||||
|   if i < 20 { | ||||
|     return b.bit-and(c).bit-or(b.bit-not().bit-and(mask-32).bit-and(d)) | ||||
|   } else if i < 40 { | ||||
|     return b.bit-xor(c).bit-xor(d) | ||||
|   } else if i < 60 { | ||||
|     let bc = b.bit-and(c) | ||||
|     let bd = b.bit-and(d) | ||||
|     let cd = c.bit-and(d) | ||||
|     return bc.bit-or(bd).bit-or(cd) | ||||
|   } else { | ||||
|     return b.bit-xor(c).bit-xor(d) | ||||
|   } | ||||
| } | ||||
|  | ||||
| /// Secure Hash Algorithm 1 | ||||
| /// ```example | ||||
| /// #bytes-to-hex(sha1("Hello World!")) | ||||
| /// ``` | ||||
| /// -> bytes | ||||
| #let sha1( | ||||
|   /// Message to hash | ||||
|   /// -> str | ||||
|   message, | ||||
|   /// Initial vector | ||||
|   /// -> array | ||||
|   iv: sha1-default-iv | ||||
| ) = { | ||||
|   // Complete message to multiple of 512 bits | ||||
|   let bin-str = "" | ||||
|   for char in bytes(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 | ||||
|   } | ||||
|   bin-str += z-fill(str(l, base: 2), 64) | ||||
|   let bin = bin-str.clusters().map(int) | ||||
|  | ||||
|   // Split into blocks of 16 32-bit words | ||||
|   let words = bin.chunks(32).map(bin-to-int) | ||||
|   let blocks = words.chunks(16) | ||||
|    | ||||
|   let vec = iv | ||||
|  | ||||
|   for block in blocks { | ||||
|     // Expand | ||||
|     for i in range(16, 80) { | ||||
|       let chosen-words = ( | ||||
|         block.at(i - 3), | ||||
|         block.at(i - 8), | ||||
|         block.at(i - 14), | ||||
|         block.at(i - 16) | ||||
|       ) | ||||
|       let word = circular-shift( | ||||
|         chosen-words.fold(0, (a, b) => a.bit-xor(b)) | ||||
|       ) | ||||
|       block.push(word) | ||||
|     } | ||||
|  | ||||
|     // Compress | ||||
|     let (A, B, C, D, E) = vec | ||||
|     for i in range(80) { | ||||
|       let temp = ( | ||||
|         circular-shift(A, n: 5) + | ||||
|         _sha1-func(i, B, C, D) + | ||||
|         E + | ||||
|         block.at(i) + | ||||
|         _sha1-const(i) | ||||
|       ) | ||||
|       temp = calc.rem(temp, max-32) | ||||
|       (A, B, C, D, E) = (temp, A, circular-shift(B, n: 30), C, D) | ||||
|     } | ||||
|     vec = vec.zip((A, B, C, D, E)).map(p => { | ||||
|       calc.rem(p.sum(), max-32) | ||||
|     }) | ||||
|   } | ||||
|    | ||||
|   let digest-bytes = () | ||||
|   for n in vec { | ||||
|     digest-bytes += ( | ||||
|       n.bit-rshift(24), | ||||
|       n.bit-rshift(16).bit-and(0xff), | ||||
|       n.bit-rshift(8).bit-and(0xff), | ||||
|       n.bit-and(0xff) | ||||
|     ) | ||||
|   } | ||||
|    | ||||
|   return bytes(digest-bytes) | ||||
| } | ||||
							
								
								
									
										100
									
								
								src/utils.typ
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										100
									
								
								src/utils.typ
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,100 @@ | ||||
| /// Applies the XOR operation between two byte arrays | ||||
| /// ```example | ||||
| /// #let a = bytes((0b010, 0b0111)) | ||||
| /// #let b = bytes((0b011, 0b0101)) | ||||
| /// #array(xor-bytes(a, b)).map( | ||||
| ///   b => z-fill(str(b, base: 2), 3) | ||||
| /// ) | ||||
| /// ``` | ||||
| /// -> bytes | ||||
| #let xor-bytes( | ||||
|   /// First byte array | ||||
|   /// -> bytes | ||||
|   bytes-a, | ||||
|   /// Second byte array | ||||
|   /// -> bytes | ||||
|   bytes-b | ||||
| ) = { | ||||
|   let length = calc.max(bytes-a.len(), bytes-b.len()) | ||||
|   let bytes-c = () | ||||
|   for i in range(length) { | ||||
|     bytes-c.push( | ||||
|       bytes-a.at(i, default: 0).bit-xor(bytes-b.at(i, default: 0)) | ||||
|     ) | ||||
|   } | ||||
|   return bytes(bytes-c) | ||||
| } | ||||
|  | ||||
| /// Pads a string with 0s on the left to reach a certain length | ||||
| /// ```example | ||||
| /// #z-fill("1011", 8) | ||||
| /// ``` | ||||
| /// -> str | ||||
| #let z-fill( | ||||
|   /// -> str | ||||
|   string, | ||||
|   /// -> number | ||||
|   length | ||||
| ) = { | ||||
|   return "0" * (length - string.len()) + string | ||||
| } | ||||
|  | ||||
| /// Converts a byte array to a hexadecimal string | ||||
| /// ```example | ||||
| /// #let b = bytes((0xfa, 0xca, 0xde)) | ||||
| /// #bytes-to-hex(b) | ||||
| /// ``` | ||||
| /// -> str | ||||
| #let bytes-to-hex( | ||||
|   /// -> bytes | ||||
|   bytes | ||||
| ) = { | ||||
|   let res = "" | ||||
|   for byte in bytes { | ||||
|     res += z-fill(str(byte, base: 16), 2) | ||||
|   } | ||||
|   return res | ||||
| } | ||||
|  | ||||
| /// Converts an array of bits into an integer | ||||
| /// ```example | ||||
| /// #let bits = (0, 0, 1, 0, 1, 0, 1, 0) | ||||
| /// #bin-to-int(bits) | ||||
| /// ``` | ||||
| /// -> number | ||||
| #let bin-to-int( | ||||
|   /// Bit array | ||||
|   /// -> array | ||||
|   bin | ||||
| ) = { | ||||
|   return bin.fold( | ||||
|     0, | ||||
|     (v, b) => v.bit-lshift(1).bit-or(b) | ||||
|   ) | ||||
| } | ||||
|  | ||||
| #let max-32 = 1.bit-lshift(32) | ||||
| #let mask-32 = max-32 - 1 | ||||
|  | ||||
| /// Rotates a number to the left (wrapping the leftmost bits to the right) | ||||
| /// ```example | ||||
| /// #let a = 42 | ||||
| /// #let b = circular-shift(a, n: 20) | ||||
| /// #let c = circular-shift(b, n: 11) | ||||
| /// #b, #c | ||||
| /// ``` | ||||
| /// -> number | ||||
| #let circular-shift( | ||||
|   /// Number to rotate | ||||
|   /// -> number | ||||
|   x, | ||||
|   /// Shift amount | ||||
|   /// -> number | ||||
|   n: 1 | ||||
| ) = { | ||||
|   if n < 0 { | ||||
|     return circular-shift(x, n: 32 + n) | ||||
|   } | ||||
|   let high-bits = x.bit-rshift(32 - n) | ||||
|   return x.bit-lshift(n).bit-or(high-bits).bit-and(mask-32) | ||||
| } | ||||
							
								
								
									
										14
									
								
								typst.toml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										14
									
								
								typst.toml
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,14 @@ | ||||
| [package] | ||||
| name = "jumble" | ||||
| version = "0.0.1" | ||||
| compiler = "0.12.0" | ||||
| repository = "https://git.kb28.ch/HEL/jumble" | ||||
| entrypoint = "src/lib.typ" | ||||
| authors = [ | ||||
|     "Louis Heredero <https://git.kb28.ch/HEL>" | ||||
| ] | ||||
| categories = ["scripting", "utility"] | ||||
| license = "Apache-2.0" | ||||
| description = "A package to hash " | ||||
| keywords = ["hash", "algorithm", "cryptography", "md5", "sha1"] | ||||
| exclude = [ "gallery", "gallery.bash", "docs" ] | ||||
		Reference in New Issue
	
	Block a user