From aa54d308542feb83bcbeec081f652ce0f3de73d2 Mon Sep 17 00:00:00 2001 From: TheAMM Date: Mon, 18 Dec 2017 19:55:31 +0200 Subject: [PATCH] Replace sha256.lua with sha1.lua (no need for 5.2 bit32) Fixes #6, version bump --- cat_osc.json | 4 +- lib/sha1.lua | 332 +++++++++++++++++++++++++++++++++++++ lib/sha256.lua | 241 --------------------------- src/options.lua | 2 +- src/thumbnailer_shared.lua | 2 +- version.txt | 2 +- 6 files changed, 337 insertions(+), 246 deletions(-) create mode 100644 lib/sha1.lua delete mode 100644 lib/sha256.lua diff --git a/cat_osc.json b/cat_osc.json index 8bbd128..0f2d7a8 100644 --- a/cat_osc.json +++ b/cat_osc.json @@ -4,7 +4,7 @@ "src/license_blurb.lua", "", "lib/helpers.lua", - "lib/sha256.lua", + "lib/sha1.lua", "src/options.lua", "src/thumbnailer_shared.lua", "src/patched_osc.lua" @@ -15,4 +15,4 @@ "header_prefix" : "--[ ", "header_suffix" : " ]--" -} \ No newline at end of file +} diff --git a/lib/sha1.lua b/lib/sha1.lua new file mode 100644 index 0000000..3057030 --- /dev/null +++ b/lib/sha1.lua @@ -0,0 +1,332 @@ +-- $Revision: 1.5 $ +-- $Date: 2014-09-10 16:54:25 $ + +-- This module was originally taken from http://cube3d.de/uploads/Main/sha1.txt. + +------------------------------------------------------------------------------- +-- SHA-1 secure hash computation, and HMAC-SHA1 signature computation, +-- in pure Lua (tested on Lua 5.1) +-- License: MIT +-- +-- Usage: +-- local hashAsHex = sha1.hex(message) -- returns a hex string +-- local hashAsData = sha1.bin(message) -- returns raw bytes +-- +-- local hmacAsHex = sha1.hmacHex(key, message) -- hex string +-- local hmacAsData = sha1.hmacBin(key, message) -- raw bytes +-- +-- +-- Pass sha1.hex() a string, and it returns a hash as a 40-character hex string. +-- For example, the call +-- +-- local hash = sha1.hex("iNTERFACEWARE") +-- +-- puts the 40-character string +-- +-- "e76705ffb88a291a0d2f9710a5471936791b4819" +-- +-- into the variable 'hash' +-- +-- Pass sha1.hmacHex() a key and a message, and it returns the signature as a +-- 40-byte hex string. +-- +-- +-- The two "bin" versions do the same, but return the 20-byte string of raw +-- data that the 40-byte hex strings represent. +-- +------------------------------------------------------------------------------- +-- +-- Description +-- Due to the lack of bitwise operations in 5.1, this version uses numbers to +-- represents the 32bit words that we combine with binary operations. The basic +-- operations of byte based "xor", "or", "and" are all cached in a combination +-- table (several 64k large tables are built on startup, which +-- consumes some memory and time). The caching can be switched off through +-- setting the local cfg_caching variable to false. +-- For all binary operations, the 32 bit numbers are split into 8 bit values +-- that are combined and then merged again. +-- +-- Algorithm: http://www.itl.nist.gov/fipspubs/fip180-1.htm +-- +------------------------------------------------------------------------------- + +local sha1 = (function() +local sha1 = {} + +-- set this to false if you don't want to build several 64k sized tables when +-- loading this file (takes a while but grants a boost of factor 13) +local cfg_caching = false +-- local storing of global functions (minor speedup) +local floor,modf = math.floor,math.modf +local char,format,rep = string.char,string.format,string.rep + +-- merge 4 bytes to an 32 bit word +local function bytes_to_w32 (a,b,c,d) return a*0x1000000+b*0x10000+c*0x100+d end +-- split a 32 bit word into four 8 bit numbers +local function w32_to_bytes (i) + return floor(i/0x1000000)%0x100,floor(i/0x10000)%0x100,floor(i/0x100)%0x100,i%0x100 +end + +-- shift the bits of a 32 bit word. Don't use negative values for "bits" +local function w32_rot (bits,a) + local b2 = 2^(32-bits) + local a,b = modf(a/b2) + return a+b*b2*(2^(bits)) +end + +-- caching function for functions that accept 2 arguments, both of values between +-- 0 and 255. The function to be cached is passed, all values are calculated +-- during loading and a function is returned that returns the cached values (only) +local function cache2arg (fn) + if not cfg_caching then return fn end + local lut = {} + for i=0,0xffff do + local a,b = floor(i/0x100),i%0x100 + lut[i] = fn(a,b) + end + return function (a,b) + return lut[a*0x100+b] + end +end + +-- splits an 8-bit number into 8 bits, returning all 8 bits as booleans +local function byte_to_bits (b) + local b = function (n) + local b = floor(b/n) + return b%2==1 + end + return b(1),b(2),b(4),b(8),b(16),b(32),b(64),b(128) +end + +-- builds an 8bit number from 8 booleans +local function bits_to_byte (a,b,c,d,e,f,g,h) + local function n(b,x) return b and x or 0 end + return n(a,1)+n(b,2)+n(c,4)+n(d,8)+n(e,16)+n(f,32)+n(g,64)+n(h,128) +end + +-- debug function for visualizing bits in a string +local function bits_to_string (a,b,c,d,e,f,g,h) + local function x(b) return b and "1" or "0" end + return ("%s%s%s%s %s%s%s%s"):format(x(a),x(b),x(c),x(d),x(e),x(f),x(g),x(h)) +end + +-- debug function for converting a 8-bit number as bit string +local function byte_to_bit_string (b) + return bits_to_string(byte_to_bits(b)) +end + +-- debug function for converting a 32 bit number as bit string +local function w32_to_bit_string(a) + if type(a) == "string" then return a end + local aa,ab,ac,ad = w32_to_bytes(a) + local s = byte_to_bit_string + return ("%s %s %s %s"):format(s(aa):reverse(),s(ab):reverse(),s(ac):reverse(),s(ad):reverse()):reverse() +end + +-- bitwise "and" function for 2 8bit number +local band = cache2arg (function(a,b) + local A,B,C,D,E,F,G,H = byte_to_bits(b) + local a,b,c,d,e,f,g,h = byte_to_bits(a) + return bits_to_byte( + A and a, B and b, C and c, D and d, + E and e, F and f, G and g, H and h) + end) + +-- bitwise "or" function for 2 8bit numbers +local bor = cache2arg(function(a,b) + local A,B,C,D,E,F,G,H = byte_to_bits(b) + local a,b,c,d,e,f,g,h = byte_to_bits(a) + return bits_to_byte( + A or a, B or b, C or c, D or d, + E or e, F or f, G or g, H or h) + end) + +-- bitwise "xor" function for 2 8bit numbers +local bxor = cache2arg(function(a,b) + local A,B,C,D,E,F,G,H = byte_to_bits(b) + local a,b,c,d,e,f,g,h = byte_to_bits(a) + return bits_to_byte( + A ~= a, B ~= b, C ~= c, D ~= d, + E ~= e, F ~= f, G ~= g, H ~= h) + end) + +-- bitwise complement for one 8bit number +local function bnot (x) + return 255-(x % 256) +end + +-- creates a function to combine to 32bit numbers using an 8bit combination function +local function w32_comb(fn) + return function (a,b) + local aa,ab,ac,ad = w32_to_bytes(a) + local ba,bb,bc,bd = w32_to_bytes(b) + return bytes_to_w32(fn(aa,ba),fn(ab,bb),fn(ac,bc),fn(ad,bd)) + end +end + +-- create functions for and, xor and or, all for 2 32bit numbers +local w32_and = w32_comb(band) +local w32_xor = w32_comb(bxor) +local w32_or = w32_comb(bor) + +-- xor function that may receive a variable number of arguments +local function w32_xor_n (a,...) + local aa,ab,ac,ad = w32_to_bytes(a) + for i=1,select('#',...) do + local ba,bb,bc,bd = w32_to_bytes(select(i,...)) + aa,ab,ac,ad = bxor(aa,ba),bxor(ab,bb),bxor(ac,bc),bxor(ad,bd) + end + return bytes_to_w32(aa,ab,ac,ad) +end + +-- combining 3 32bit numbers through binary "or" operation +local function w32_or3 (a,b,c) + local aa,ab,ac,ad = w32_to_bytes(a) + local ba,bb,bc,bd = w32_to_bytes(b) + local ca,cb,cc,cd = w32_to_bytes(c) + return bytes_to_w32( + bor(aa,bor(ba,ca)), bor(ab,bor(bb,cb)), bor(ac,bor(bc,cc)), bor(ad,bor(bd,cd)) + ) +end + +-- binary complement for 32bit numbers +local function w32_not (a) + return 4294967295-(a % 4294967296) +end + +-- adding 2 32bit numbers, cutting off the remainder on 33th bit +local function w32_add (a,b) return (a+b) % 4294967296 end + +-- adding n 32bit numbers, cutting off the remainder (again) +local function w32_add_n (a,...) + for i=1,select('#',...) do + a = (a+select(i,...)) % 4294967296 + end + return a +end +-- converting the number to a hexadecimal string +local function w32_to_hexstring (w) return format("%08x",w) end + +-- calculating the SHA1 for some text +function sha1.hex(msg) + local H0,H1,H2,H3,H4 = 0x67452301,0xEFCDAB89,0x98BADCFE,0x10325476,0xC3D2E1F0 + local msg_len_in_bits = #msg * 8 + + local first_append = char(0x80) -- append a '1' bit plus seven '0' bits + + local non_zero_message_bytes = #msg +1 +8 -- the +1 is the appended bit 1, the +8 are for the final appended length + local current_mod = non_zero_message_bytes % 64 + local second_append = current_mod>0 and rep(char(0), 64 - current_mod) or "" + + -- now to append the length as a 64-bit number. + local B1, R1 = modf(msg_len_in_bits / 0x01000000) + local B2, R2 = modf( 0x01000000 * R1 / 0x00010000) + local B3, R3 = modf( 0x00010000 * R2 / 0x00000100) + local B4 = 0x00000100 * R3 + + local L64 = char( 0) .. char( 0) .. char( 0) .. char( 0) -- high 32 bits + .. char(B1) .. char(B2) .. char(B3) .. char(B4) -- low 32 bits + + msg = msg .. first_append .. second_append .. L64 + + assert(#msg % 64 == 0) + + local chunks = #msg / 64 + + local W = { } + local start, A, B, C, D, E, f, K, TEMP + local chunk = 0 + + while chunk < chunks do + -- + -- break chunk up into W[0] through W[15] + -- + start,chunk = chunk * 64 + 1,chunk + 1 + + for t = 0, 15 do + W[t] = bytes_to_w32(msg:byte(start, start + 3)) + start = start + 4 + end + + -- + -- build W[16] through W[79] + -- + for t = 16, 79 do + -- For t = 16 to 79 let Wt = S1(Wt-3 XOR Wt-8 XOR Wt-14 XOR Wt-16). + W[t] = w32_rot(1, w32_xor_n(W[t-3], W[t-8], W[t-14], W[t-16])) + end + + A,B,C,D,E = H0,H1,H2,H3,H4 + + for t = 0, 79 do + if t <= 19 then + -- (B AND C) OR ((NOT B) AND D) + f = w32_or(w32_and(B, C), w32_and(w32_not(B), D)) + K = 0x5A827999 + elseif t <= 39 then + -- B XOR C XOR D + f = w32_xor_n(B, C, D) + K = 0x6ED9EBA1 + elseif t <= 59 then + -- (B AND C) OR (B AND D) OR (C AND D + f = w32_or3(w32_and(B, C), w32_and(B, D), w32_and(C, D)) + K = 0x8F1BBCDC + else + -- B XOR C XOR D + f = w32_xor_n(B, C, D) + K = 0xCA62C1D6 + end + + -- TEMP = S5(A) + ft(B,C,D) + E + Wt + Kt; + A,B,C,D,E = w32_add_n(w32_rot(5, A), f, E, W[t], K), + A, w32_rot(30, B), C, D + end + -- Let H0 = H0 + A, H1 = H1 + B, H2 = H2 + C, H3 = H3 + D, H4 = H4 + E. + H0,H1,H2,H3,H4 = w32_add(H0, A),w32_add(H1, B),w32_add(H2, C),w32_add(H3, D),w32_add(H4, E) + end + local f = w32_to_hexstring + return f(H0) .. f(H1) .. f(H2) .. f(H3) .. f(H4) +end + +local function hex_to_binary(hex) + return hex:gsub('..', function(hexval) + return string.char(tonumber(hexval, 16)) + end) +end + +function sha1.bin(msg) + return hex_to_binary(sha1.hex(msg)) +end + +local xor_with_0x5c = {} +local xor_with_0x36 = {} +-- building the lookuptables ahead of time (instead of littering the source code +-- with precalculated values) +for i=0,0xff do + xor_with_0x5c[char(i)] = char(bxor(i,0x5c)) + xor_with_0x36[char(i)] = char(bxor(i,0x36)) +end + +local blocksize = 64 -- 512 bits + +function sha1.hmacHex(key, text) + assert(type(key) == 'string', "key passed to hmacHex should be a string") + assert(type(text) == 'string', "text passed to hmacHex should be a string") + + if #key > blocksize then + key = sha1.bin(key) + end + + local key_xord_with_0x36 = key:gsub('.', xor_with_0x36) .. string.rep(string.char(0x36), blocksize - #key) + local key_xord_with_0x5c = key:gsub('.', xor_with_0x5c) .. string.rep(string.char(0x5c), blocksize - #key) + + return sha1.hex(key_xord_with_0x5c .. sha1.bin(key_xord_with_0x36 .. text)) +end + +function sha1.hmacBin(key, text) + return hex_to_binary(sha1.hmacHex(key, text)) +end + +return sha1 +end)() + diff --git a/lib/sha256.lua b/lib/sha256.lua deleted file mode 100644 index 80e3a42..0000000 --- a/lib/sha256.lua +++ /dev/null @@ -1,241 +0,0 @@ --- SHA-256 code in Lua 5.2; based on the pseudo-code from --- Wikipedia (http://en.wikipedia.org/wiki/SHA-2) --- The original code here was written by Roberto Ierusalimschy, and is licensed under MIT (see http://lua-users.org/lists/lua-l/2014-08/msg00628.html) --- This version has been slightly modified for mpv_thumbnail_script.lua - -local _sha = function() - local band, rrotate, bxor, rshift, bnot = - bit32.band, bit32.rrotate, bit32.bxor, bit32.rshift, bit32.bnot - - -- Initialize table of round constants - -- (first 32 bits of the fractional parts of the cube roots of the first - -- 64 primes 2..311): - local k = { - 0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, - 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5, - 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, - 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174, - 0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, - 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da, - 0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, - 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967, - 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, - 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85, - 0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, - 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070, - 0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, - 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3, - 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, - 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2, - } - - - -- transform a string of bytes in a string of hexadecimal digits - local function str2hexa (s) - local h = string.gsub(s, ".", function(c) - return string.format("%02x", string.byte(c)) - end) - return h - end - - - -- transform number 'l' in a big-endian sequence of 'n' bytes - -- (coded as a string) - local function num2s (l, n) - local s = "" - for i = 1, n do - local rem = l % 256 - s = string.char(rem) .. s - l = (l - rem) / 256 - end - return s - end - - -- transform the big-endian sequence of four bytes starting at - -- index 'i' in 's' into a number - local function s232num (s, i) - local n = 0 - for i = i, i + 3 do - n = n*256 + string.byte(s, i) - end - return n - end - - - -- append the bit '1' to the message - -- append k bits '0', where k is the minimum number >= 0 such that the - -- resulting message length (in bits) is congruent to 448 (mod 512) - -- append length of message (before pre-processing), in bits, as 64-bit - -- big-endian integer - local function preproc (msg, len) - local extra = -(len + 1 + 8) % 64 - len = num2s(8 * len, 8) -- original len in bits, coded - msg = msg .. "\128" .. string.rep("\0", extra) .. len - assert(#msg % 64 == 0) - return msg - end - - - local function initH224 (H) - -- (second 32 bits of the fractional parts of the square roots of the - -- 9th through 16th primes 23..53) - H[1] = 0xc1059ed8 - H[2] = 0x367cd507 - H[3] = 0x3070dd17 - H[4] = 0xf70e5939 - H[5] = 0xffc00b31 - H[6] = 0x68581511 - H[7] = 0x64f98fa7 - H[8] = 0xbefa4fa4 - return H - end - - - local function initH256 (H) - -- (first 32 bits of the fractional parts of the square roots of the - -- first 8 primes 2..19): - H[1] = 0x6a09e667 - H[2] = 0xbb67ae85 - H[3] = 0x3c6ef372 - H[4] = 0xa54ff53a - H[5] = 0x510e527f - H[6] = 0x9b05688c - H[7] = 0x1f83d9ab - H[8] = 0x5be0cd19 - return H - end - - - local function digestblock (msg, i, H) - - -- break chunk into sixteen 32-bit big-endian words w[1..16] - local w = {} - for j = 1, 16 do - w[j] = s232num(msg, i + (j - 1)*4) - end - - -- Extend the sixteen 32-bit words into sixty-four 32-bit words: - for j = 17, 64 do - local v = w[j - 15] - local s0 = bxor(rrotate(v, 7), rrotate(v, 18), rshift(v, 3)) - v = w[j - 2] - local s1 = bxor(rrotate(v, 17), rrotate(v, 19), rshift(v, 10)) - w[j] = w[j - 16] + s0 + w[j - 7] + s1 - end - - -- Initialize hash value for this chunk: - local a, b, c, d, e, f, g, h = - H[1], H[2], H[3], H[4], H[5], H[6], H[7], H[8] - - -- Main loop: - for i = 1, 64 do - local s0 = bxor(rrotate(a, 2), rrotate(a, 13), rrotate(a, 22)) - local maj = bxor(band(a, b), band(a, c), band(b, c)) - local t2 = s0 + maj - local s1 = bxor(rrotate(e, 6), rrotate(e, 11), rrotate(e, 25)) - local ch = bxor (band(e, f), band(bnot(e), g)) - local t1 = h + s1 + ch + k[i] + w[i] - - h = g - g = f - f = e - e = d + t1 - d = c - c = b - b = a - a = t1 + t2 - end - - -- Add (mod 2^32) this chunk's hash to result so far: - H[1] = band(H[1] + a) - H[2] = band(H[2] + b) - H[3] = band(H[3] + c) - H[4] = band(H[4] + d) - H[5] = band(H[5] + e) - H[6] = band(H[6] + f) - H[7] = band(H[7] + g) - H[8] = band(H[8] + h) - - end - - - local function finalresult224 (H) - -- Produce the final hash value (big-endian): - return - str2hexa(num2s(H[1], 4)..num2s(H[2], 4)..num2s(H[3], 4)..num2s(H[4], 4).. - num2s(H[5], 4)..num2s(H[6], 4)..num2s(H[7], 4)) - end - - - local function finalresult256 (H) - -- Produce the final hash value (big-endian): - return - str2hexa(num2s(H[1], 4)..num2s(H[2], 4)..num2s(H[3], 4)..num2s(H[4], 4).. - num2s(H[5], 4)..num2s(H[6], 4)..num2s(H[7], 4)..num2s(H[8], 4)) - end - - - ---------------------------------------------------------------------- - local HH = {} -- to reuse - - local function hash224 (msg) - msg = preproc(msg, #msg) - local H = initH224(HH) - - -- Process the message in successive 512-bit (64 bytes) chunks: - for i = 1, #msg, 64 do - digestblock(msg, i, H) - end - - return finalresult224(H) - end - - - local function hash256 (msg) - msg = preproc(msg, #msg) - local H = initH256(HH) - - -- Process the message in successive 512-bit (64 bytes) chunks: - for i = 1, #msg, 64 do - digestblock(msg, i, H) - end - - return finalresult256(H) - end - ---------------------------------------------------------------------- - local mt = {} - - local function new256 () - local o = {H = initH256({}), msg = "", len = 0} - setmetatable(o, mt) - return o - end - - mt.__index = mt - - function mt:add (m) - self.msg = self.msg .. m - self.len = self.len + #m - local t = 0 - while #self.msg - t >= 64 do - digestblock(self.msg, t + 1, self.H) - t = t + 64 - end - self.msg = self.msg:sub(t + 1, -1) - end - - - function mt:close () - self.msg = preproc(self.msg, self.len) - self:add("") - return finalresult256(self.H) - end - ---------------------------------------------------------------------- - - return { - hash224 = hash224, - hash256 = hash256, - new256 = new256, - } -end -local sha256 = _sha() diff --git a/src/options.lua b/src/options.lua index 80735bb..94d5368 100644 --- a/src/options.lua +++ b/src/options.lua @@ -12,7 +12,7 @@ local thumbnailer_options = { -- Only automatically thumbnail videos shorter than this (seconds) autogenerate_max_duration = 3600, -- 1 hour - -- MD5-sum filenames over this length + -- SHA1-sum filenames over this length -- It's nice to know what files the thumbnails are (hence directory names) -- but long URLs may approach filesystem limits. hash_filename_length = 128, diff --git a/src/thumbnailer_shared.lua b/src/thumbnailer_shared.lua index 170d506..3bb5299 100644 --- a/src/thumbnailer_shared.lua +++ b/src/thumbnailer_shared.lua @@ -101,7 +101,7 @@ function Thumbnailer:get_thumbnail_template() filename = filename:gsub('[^a-zA-Z0-9_.%-\' ]', '') -- Hash overly long filenames (most likely URLs) if #filename > thumbnailer_options.hash_filename_length then - filename = sha256.hash256(filename) + filename = sha1.hex(filename) end local file_key = ("%s-%d"):format(filename, filesize) diff --git a/version.txt b/version.txt index d15723f..1c09c74 100644 --- a/version.txt +++ b/version.txt @@ -1 +1 @@ -0.3.2 +0.3.3