Skip to content

Commit

Permalink
Merge pull request #696 from tsuyoshicho/feature/hashupdate
Browse files Browse the repository at this point in the history
Proposal: update and simply refine Hash(MD5,SHA1) module; using Data.List.Byte functions
  • Loading branch information
ujihisa committed Nov 19, 2019
2 parents 32f20a1 + 3574028 commit 184a157
Show file tree
Hide file tree
Showing 4 changed files with 75 additions and 96 deletions.
64 changes: 23 additions & 41 deletions autoload/vital/__vital__/Hash/MD5.vim
Expand Up @@ -3,13 +3,19 @@
let s:save_cpo = &cpo
set cpo&vim

function! s:_vital_created(module) abort
let a:module.name = 'MD5'
let a:module.hash_length = 128
endfunction

function! s:_vital_loaded(V) abort
let s:V = a:V
let s:bitwise = s:V.import('Bitwise')
let s:Bitwise = s:V.import('Bitwise')
let s:ByteArray = s:V.import('Data.List.Byte')
endfunction

function! s:_vital_depends() abort
return ['Bitwise']
return ['Bitwise', 'Data.List.Byte']
endfunction

let s:shift = [
Expand Down Expand Up @@ -39,16 +45,16 @@ let s:table = [
\ ]

function! s:sum(data) abort
let bytes = s:_str2bytes(a:data)
let bytes = s:ByteArray.from_string(a:data)
return s:sum_raw(bytes)
endfunction

function! s:sum_raw(bytes) abort
return s:_bytes2binstr(s:digest_raw(a:bytes))
return s:ByteArray.to_hexstring(s:digest_raw(a:bytes))
endfunction

function! s:digest(data) abort
let bytes = s:_str2bytes(a:data)
let bytes = s:ByteArray.from_string(a:data)
return s:digest_raw(bytes)
endfunction

Expand All @@ -67,16 +73,16 @@ function! s:digest_raw(bytes) abort


if has('num64')
call extend(l:padded, s:_int2bytes(64, l:orig_len))
call extend(l:padded, s:ByteArray.endian_convert(s:ByteArray.from_int(l:orig_len, 64)))
else
call extend(l:padded, s:_int2bytes(32, l:orig_len))
call extend(l:padded, s:ByteArray.endian_convert(s:ByteArray.from_int(l:orig_len, 32)))
call extend(l:padded, [0, 0, 0, 0])
endif

for l:chunk_i in range(0, len(l:padded)-1, 64)
let l:chunk = l:padded[l:chunk_i : l:chunk_i + 63]

let l:M = map(range(16), 's:_bytes2int32(l:chunk[(v:val*4):(v:val*4)+3])')
let l:M = map(range(16), {i,v -> s:ByteArray.to_int(s:ByteArray.endian_convert(l:chunk[(i*4) : (i*4)+3]))})
let l:A = l:a0
let l:B = l:b0
let l:C = l:c0
Expand All @@ -86,24 +92,24 @@ function! s:digest_raw(bytes) abort
let l:F = 0
let l:g = 0
if 0 <= l:i && l:i <= 15
let l:F = s:bitwise.or(s:bitwise.and(l:B, l:C), s:bitwise.and(s:bitwise.invert(l:B), l:D))
let l:F = s:Bitwise.or(s:Bitwise.and(l:B, l:C), s:Bitwise.and(s:Bitwise.invert(l:B), l:D))
let l:g = l:i
elseif 16 <= l:i && l:i <= 31
let l:F = s:bitwise.or(s:bitwise.and(l:B, l:D), s:bitwise.and(s:bitwise.invert(l:D), l:C))
let l:F = s:Bitwise.or(s:Bitwise.and(l:B, l:D), s:Bitwise.and(s:Bitwise.invert(l:D), l:C))
let l:g = fmod((5 * l:i) + 1, 16)
elseif 32 <= l:i && l:i <= 47
let l:F = s:bitwise.xor(l:B, s:bitwise.xor(l:C, l:D))
let l:F = s:Bitwise.xor(l:B, s:Bitwise.xor(l:C, l:D))
let l:g = fmod((3 * l:i) + 5, 16)
elseif 48 <= l:i && l:i <= 63
let l:F = s:bitwise.xor(l:C, s:bitwise.or(l:B, s:bitwise.invert(l:D)))
let l:F = s:Bitwise.xor(l:C, s:Bitwise.or(l:B, s:Bitwise.invert(l:D)))
let l:g = fmod(7 * l:i, 16)
endif

let l:F = l:F + l:A + s:table[l:i] + M[float2nr(l:g)]
let l:A = l:D
let l:D = l:C
let l:C = l:B
let l:B = l:B + s:_leftrotate(l:F, s:shift[l:i])
let l:B = l:B + s:Bitwise.rotate32l(l:F, s:shift[l:i])

endfor
let l:a0 = l:a0 + l:A
Expand All @@ -113,38 +119,14 @@ function! s:digest_raw(bytes) abort
endfor

let l:bytes = []
call extend(l:bytes, s:_int2bytes(32, l:a0))
call extend(l:bytes, s:_int2bytes(32, l:b0))
call extend(l:bytes, s:_int2bytes(32, l:c0))
call extend(l:bytes, s:_int2bytes(32, l:d0))
call extend(l:bytes, s:ByteArray.endian_convert(s:ByteArray.from_int(l:a0, 32)))
call extend(l:bytes, s:ByteArray.endian_convert(s:ByteArray.from_int(l:b0, 32)))
call extend(l:bytes, s:ByteArray.endian_convert(s:ByteArray.from_int(l:c0, 32)))
call extend(l:bytes, s:ByteArray.endian_convert(s:ByteArray.from_int(l:d0, 32)))

return l:bytes
endfunction

function! s:_leftrotate(x, c) abort
let l:x = s:bitwise.and(a:x, 0xFFFFFFFF)
return s:bitwise.and(s:bitwise.or(s:bitwise.lshift(l:x, a:c), s:bitwise.rshift(l:x, (32-a:c))), 0xFFFFFFFF)
endfunction

function! s:_bytes2binstr(bytes) abort
return join(map(copy(a:bytes), 'printf(''%02x'', v:val)'), '')
endfunction

function! s:_str2bytes(str) abort
return map(range(len(a:str)), 'char2nr(a:str[v:val])')
endfunction

function! s:_int2bytes(bits, int) abort
return map(range(a:bits / 8), 's:bitwise.and(s:bitwise.rshift(a:int, v:val * 8), 0xff)')
endfunction

function! s:_bytes2int32(bytes) abort
return s:bitwise.or(s:bitwise.lshift(a:bytes[3], 24),
\ s:bitwise.or(s:bitwise.lshift(a:bytes[2], 16),
\ s:bitwise.or(s:bitwise.lshift(a:bytes[1], 8),
\ a:bytes[0])))
endfunction

let &cpo = s:save_cpo
unlet s:save_cpo

Expand Down
89 changes: 38 additions & 51 deletions autoload/vital/__vital__/Hash/SHA1.vim
Expand Up @@ -8,26 +8,32 @@
let s:save_cpo = &cpo
set cpo&vim

function! s:_vital_created(module) abort
let a:module.name = 'SHA1'
let a:module.hash_length = s:sha1hashsize * 8 " 160
endfunction

function! s:_vital_loaded(V) abort
let s:V = a:V
let s:bitwise = s:V.import('Bitwise')
let s:Bitwise = s:V.import('Bitwise')
let s:ByteArray = s:V.import('Data.List.Byte')
endfunction

function! s:_vital_depends() abort
return ['Bitwise']
return ['Bitwise', 'Data.List.Byte']
endfunction

function! s:sum(data) abort
let bytes = s:_str2bytes(a:data)
let bytes = s:ByteArray.from_string(a:data)
return s:sum_raw(bytes)
endfunction

function! s:sum_raw(bytes) abort
return s:_bytes2binstr(s:digest_raw(a:bytes))
return s:ByteArray.to_hexstring(s:digest_raw(a:bytes))
endfunction

function! s:digest(data) abort
let bytes = s:_str2bytes(a:data)
let bytes = s:ByteArray.from_string(a:data)
return s:digest_raw(bytes)
endfunction

Expand Down Expand Up @@ -77,7 +83,7 @@ let s:sha1context = {
\}

function! s:_sha1circular_shift(bits, word) abort
return s:bitwise.or(s:bitwise.lshift32(a:word, a:bits), s:bitwise.rshift32(a:word, 32 - a:bits))
return s:Bitwise.rotate32l(a:word, a:bits)
endfunction

function! s:sha1context.init() dict abort
Expand Down Expand Up @@ -112,10 +118,10 @@ function! s:sha1context.result(digest) dict abort
endif

for i in range(s:sha1hashsize)
let a:digest[i] = s:_uint8(
\ s:bitwise.rshift32(
\ self.intermediatehash[s:bitwise.rshift32(i, 2)],
\ 8 * (3 - s:bitwise.and(i, 0x03))
let a:digest[i] = s:Bitwise.uint8(
\ s:Bitwise.rshift32(
\ self.intermediatehash[s:Bitwise.rshift32(i, 2)],
\ 8 * (3 - s:Bitwise.and(i, 0x03))
\ )
\ )
endfor
Expand Down Expand Up @@ -144,7 +150,7 @@ function! s:sha1context.input(bytes) dict abort
if self.Corrupted
break
endif
call self.messageblock.push(s:_uint8(x))
call self.messageblock.push(s:Bitwise.uint8(x))

if self.messageblock.index == s:sha1blocksize
call self.process()
Expand All @@ -171,14 +177,14 @@ function! s:sha1context.process() dict abort
" Initialize the first 16 words in the array W
"
for t in range(16)
let W[t] = s:bitwise.lshift32(self.messageblock.data[t * 4], 24)
let W[t] = s:bitwise.or(W[t], s:bitwise.lshift32(self.messageblock.data[t * 4 + 1], 16))
let W[t] = s:bitwise.or(W[t], s:bitwise.lshift32(self.messageblock.data[t * 4 + 2], 8))
let W[t] = s:bitwise.or(W[t], self.messageblock.data[t * 4 + 3])
let W[t] = s:Bitwise.lshift32(self.messageblock.data[t * 4], 24)
let W[t] = s:Bitwise.or(W[t], s:Bitwise.lshift32(self.messageblock.data[t * 4 + 1], 16))
let W[t] = s:Bitwise.or(W[t], s:Bitwise.lshift32(self.messageblock.data[t * 4 + 2], 8))
let W[t] = s:Bitwise.or(W[t], self.messageblock.data[t * 4 + 3])
endfor

for t in range(16, 79)
let W[t] = s:_sha1circular_shift(1, s:bitwise.xor(s:bitwise.xor(s:bitwise.xor(W[t-3], W[t-8]), W[t-14]), W[t-16]))
let W[t] = s:_sha1circular_shift(1, s:Bitwise.xor(s:Bitwise.xor(s:Bitwise.xor(W[t-3], W[t-8]), W[t-14]), W[t-16]))
endfor

let A = self.intermediatehash[0]
Expand All @@ -189,7 +195,7 @@ function! s:sha1context.process() dict abort

for t in range(20)
let temp = s:_sha1circular_shift(5,A) +
\ s:bitwise.or(s:bitwise.and(B, C), s:bitwise.and(s:bitwise.invert(B), D)) +
\ s:Bitwise.or(s:Bitwise.and(B, C), s:Bitwise.and(s:Bitwise.invert(B), D)) +
\ E + W[t] + K[0]
let E = D
let D = C
Expand All @@ -199,7 +205,7 @@ function! s:sha1context.process() dict abort
endfor

for t in range(20, 39)
let temp = s:_sha1circular_shift(5,A) + s:bitwise.xor(s:bitwise.xor(B, C), D) + E + W[t] + K[1]
let temp = s:_sha1circular_shift(5,A) + s:Bitwise.xor(s:Bitwise.xor(B, C), D) + E + W[t] + K[1]
let E = D
let D = C
let C = s:_sha1circular_shift(30,B)
Expand All @@ -209,7 +215,7 @@ function! s:sha1context.process() dict abort

for t in range(40, 59)
let temp = s:_sha1circular_shift(5,A) +
\ s:bitwise.or(s:bitwise.or(s:bitwise.and(B, C), s:bitwise.and(B, D)), s:bitwise.and(C, D)) +
\ s:Bitwise.or(s:Bitwise.or(s:Bitwise.and(B, C), s:Bitwise.and(B, D)), s:Bitwise.and(C, D)) +
\ E + W[t] + K[2]
let E = D
let D = C
Expand All @@ -220,7 +226,7 @@ function! s:sha1context.process() dict abort

for t in range(60, 79)
let temp = s:_sha1circular_shift(5,A) +
\ s:bitwise.xor(s:bitwise.xor(B, C), D) + E + W[t] + K[3]
\ s:Bitwise.xor(s:Bitwise.xor(B, C), D) + E + W[t] + K[3]
let E = D
let D = C
let C = s:_sha1circular_shift(30,B)
Expand Down Expand Up @@ -266,14 +272,14 @@ function! s:sha1context.padding() dict abort
" Store the message length as the last 8 octets
"
" as data[-8]..data[-1]
let self.messageblock.data[56] = s:_uint8(s:bitwise.rshift32(self.length.high, 24))
let self.messageblock.data[57] = s:_uint8(s:bitwise.rshift32(self.length.high, 16))
let self.messageblock.data[58] = s:_uint8(s:bitwise.rshift32(self.length.high, 8))
let self.messageblock.data[59] = s:_uint8( self.length.high )
let self.messageblock.data[60] = s:_uint8(s:bitwise.rshift32(self.length.low , 24))
let self.messageblock.data[61] = s:_uint8(s:bitwise.rshift32(self.length.low , 16))
let self.messageblock.data[62] = s:_uint8(s:bitwise.rshift32(self.length.low , 8))
let self.messageblock.data[63] = s:_uint8( self.length.low )
let self.messageblock.data[56] = s:Bitwise.uint8(s:Bitwise.rshift32(self.length.high, 24))
let self.messageblock.data[57] = s:Bitwise.uint8(s:Bitwise.rshift32(self.length.high, 16))
let self.messageblock.data[58] = s:Bitwise.uint8(s:Bitwise.rshift32(self.length.high, 8))
let self.messageblock.data[59] = s:Bitwise.uint8( self.length.high )
let self.messageblock.data[60] = s:Bitwise.uint8(s:Bitwise.rshift32(self.length.low , 24))
let self.messageblock.data[61] = s:Bitwise.uint8(s:Bitwise.rshift32(self.length.low , 16))
let self.messageblock.data[62] = s:Bitwise.uint8(s:Bitwise.rshift32(self.length.low , 8))
let self.messageblock.data[63] = s:Bitwise.uint8( self.length.low )

call self.process()
endfunction
Expand Down Expand Up @@ -305,38 +311,19 @@ function! s:sha1context.length.sizeset(data) dict abort
" 3.b high shift = 0x0000000l >> (32 - 3)
" endif
" 32/64bit work use shift
let self.high = s:_uint32(s:bitwise.rshift(len(a:data), 32 - 3))
let self.low = s:_uint32(s:bitwise.lshift(len(a:data), 3))
let self.high = s:Bitwise.uint32(s:Bitwise.rshift(len(a:data), 32 - 3))
let self.low = s:Bitwise.uint32(s:Bitwise.lshift(len(a:data), 3))

" SHA1 2^64 - 1 overflow check
" 0xh0000000 is not 0, then overflow it(byte data are Vim List;it can contain 2^64 - 1 item)
if (has('num64') && (0 != s:_uint32(s:bitwise.rshift(len(a:data), 32 + (32 - 3)))))
" 0xh0000000 is not 0, then overflow it(byte data are Vim List;it can contains 2^64 - 1 item)
if (has('num64') && (0 != s:Bitwise.uint32(s:Bitwise.rshift(len(a:data), 32 + (32 - 3)))))
let self.high = 0
let self.low = 0
return s:input_long
endif
return s:success
endfunction

"---------------------------------------------------------------------
" misc

function! s:_uint8(n) abort
return s:bitwise.and(a:n, 0xFF)
endfunction

function! s:_uint32(n) abort
return s:bitwise.and(a:n, 0xFFFFFFFF)
endfunction

function! s:_str2bytes(str) abort
return map(range(len(a:str)), 'char2nr(a:str[v:val])')
endfunction

function! s:_bytes2binstr(bytes) abort
return join(map(copy(a:bytes), 'printf(''%02x'', v:val)'), '')
endfunction

let &cpo = s:save_cpo
unlet s:save_cpo

Expand Down
5 changes: 5 additions & 0 deletions test/Hash/MD5.vim
Expand Up @@ -9,6 +9,11 @@ function! s:suite.after()
unlet! s:MD5
endfunction

function! s:suite.prop() abort
call s:assert.is_string(s:MD5.name)
call s:assert.is_number(s:MD5.hash_length)
endfunction

function! s:suite.encode() abort
call s:assert.equal(s:MD5.sum(''), 'd41d8cd98f00b204e9800998ecf8427e')
call s:assert.equal(s:MD5.sum('a'), '0cc175b9c0f1b6a831c399e269772661')
Expand Down
13 changes: 9 additions & 4 deletions test/Hash/SHA1.vim
Expand Up @@ -9,6 +9,11 @@ function! s:suite.after()
unlet! s:SHA1
endfunction

function! s:suite.prop() abort
call s:assert.is_string(s:SHA1.name)
call s:assert.is_number(s:SHA1.hash_length)
endfunction

function! s:suite.encode() abort
call s:assert.equal(s:SHA1.sum(''), 'da39a3ee5e6b4b0d3255bfef95601890afd80709')
call s:assert.equal(s:SHA1.sum('a'), '86f7e437faa5a7fce15d1ddcb9eaeaea377667b8')
Expand Down Expand Up @@ -36,10 +41,10 @@ function! s:suite.encode() abort
\ ]
" original result
" let s:resultarray = [
" \ 'a9993e364706816aba3e25717850c26c9cd0d89d",
" \ '84983e441c3bd26ebaae4aa1f95129e5e54670f1",
" \ '34aa973cd4c4daa4f61eeb2bdbad27316534016f",
" \ 'dea356a2cddd90c7a7ecedc5ebb563934f460452"
" \ 'a9993e364706816aba3e25717850c26c9cd0d89d',
" \ '84983e441c3bd26ebaae4aa1f95129e5e54670f1',
" \ '34aa973cd4c4daa4f61eeb2bdbad27316534016f',
" \ 'dea356a2cddd90c7a7ecedc5ebb563934f460452'
" \ ]

" other SHA1 test site generate result
Expand Down

0 comments on commit 184a157

Please sign in to comment.