sha256sum.sh 5.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182
  1. #!/bin/sh
  2. # -*- mode: sh; indent-tabs-mode:nil; sh-basic-offset:2 -*-
  3. #
  4. # An implementation of the sha256sum using only POSIX shell and
  5. # POSIX utilities.
  6. #
  7. # Requires: sed, nl, xargs, printf, mktemp, stat, od, bc
  8. #
  9. # Copyright © 2020 Eric Bavier <bavier@posteo.net>
  10. # Update: Tue 18 Feb 2020 05:12:46 PM CST
  11. # Update: Fri 06 Mar 2020 12:15:50 AM CST, rewrite w/ printf, od, and bc
  12. # Update: Sat 07 Mar 2020 01:22:20 AM CST, speed up bc bitwise operations
  13. # Update: Fri 03 Apr 2020 06:38:59 PM CDT, fix msg sched init and var shadowing
  14. # Update: Thu 09 Apr 2020 10:54:39 PM CDT, accept multiple filename arguments
  15. # Update: Thu 07 Jan 2021 11:37:34 PM CST, small speedup with precomputed 2^n
  16. # License: GPLv3+
  17. #####
  18. # Note 0: Implementation from https://en.wikipedia.org/wiki/SHA-2#Pseudocode
  19. # Note 1: All variables are 32 bit unsigned integers and addition is
  20. # calculated modulo 2^32
  21. # Note 2: For each round, there is one round constant k[i] and one
  22. # entry in the message schedule array w[i], 0 ≤ i ≤ 63
  23. # Note 3: The compression function uses 8 working variables, a through h
  24. # Note 4: Big-endian convention is used when expressing the constants
  25. # in this pseudocode, and when parsing message block data from
  26. # bytes to words, for example, the first word of the input
  27. # message "abc" after padding is 0x61626380
  28. # Note 5: This implementation is extremely inefficient...
  29. fn_values_to_array(){
  30. sed 's/[^ ]\+/0x&/g
  31. s/ */\n/g' \
  32. | nl -v0 \
  33. | xargs printf "${1:0:1}[%d] = %d; "
  34. }
  35. fn_quote_hex(){
  36. sed 's/.*/0000000000000000&/ # left-pad so output is a full 64-bits
  37. s/.*\(.\{16\}\)/\1/ # trim to 16 nibbles
  38. :a
  39. s/^\([^ ]*\)\([^ ][^ ]\)/\1 \\\\x\2/
  40. t a
  41. s/^[^ ] /\\\\x& /'
  42. }
  43. constants=$(fn_values_to_array "k" <<EOF
  44. 428a2f98 71374491 b5c0fbcf e9b5dba5 3956c25b 59f111f1 923f82a4 ab1c5ed5
  45. d807aa98 12835b01 243185be 550c7dc3 72be5d74 80deb1fe 9bdc06a7 c19bf174
  46. e49b69c1 efbe4786 0fc19dc6 240ca1cc 2de92c6f 4a7484aa 5cb0a9dc 76f988da
  47. 983e5152 a831c66d b00327c8 bf597fc7 c6e00bf3 d5a79147 06ca6351 14292967
  48. 27b70a85 2e1b2138 4d2c6dfc 53380d13 650a7354 766a0abb 81c2c92e 92722c85
  49. a2bfe8a1 a81a664b c24b8b70 c76c51a3 d192e819 d6990624 f40e3585 106aa070
  50. 19a4c116 1e376c08 2748774c 34b0bcb5 391c0cb3 4ed8aa4a 5b9cca4f 682e6ff3
  51. 748f82ee 78a5636f 84c87814 8cc70208 90befffa a4506ceb bef9a3f7 c67178f2
  52. EOF
  53. )
  54. values=$(mktemp -p "${TMPDIR:-.}") || exit 1; trap 'rm $values' EXIT
  55. while test "$1" ; do
  56. # Initialize hash values:
  57. # (first 32 bits of the fractional parts of the square roots of the first 8 primes 2..19):
  58. h0=$(printf "%d" 0x6a09e667)
  59. h1=$(printf "%d" 0xbb67ae85)
  60. h2=$(printf "%d" 0x3c6ef372)
  61. h3=$(printf "%d" 0xa54ff53a)
  62. h4=$(printf "%d" 0x510e527f)
  63. h5=$(printf "%d" 0x9b05688c)
  64. h6=$(printf "%d" 0x1f83d9ab)
  65. h7=$(printf "%d" 0x5be0cd19)
  66. size=$(stat --format="%s * 8" "$1" | bc)
  67. padding=$(printf "t=64-(($size/8)%%64);if(t<9){t+55}else{t-9}\n" | bc)
  68. (cat "$1";
  69. printf "\x80";
  70. printf "%.0s\x00" $(seq 1 $padding);
  71. printf "%x" $size | fn_quote_hex | xargs printf "%b") \
  72. | od --address-radix=none --endian=big \
  73. --format=x4 --width=64 --output-duplicates \
  74. | while read chunk ; do
  75. # CHUNK contains 16 64-bit hex values
  76. message_schedule=$(printf "$chunk\n" | fn_values_to_array "w")
  77. # Inject arrays into bc for the update step.
  78. bc >$values <<EOF
  79. $constants
  80. $message_schedule
  81. scale = 0; ibase = A
  82. j = 32 # bit-width
  83. z = 2^32
  84. t[0] = 1 # powers-of-two
  85. for (i=1; i<=32; ++i)
  86. t[i] = t[i-1] * 2
  87. define p(x,y){ /* plus modulo 2^32 */
  88. scale=0; return (x + y) % z; }
  89. define g(x,n){ /* bitwise rightshift */
  90. scale=0; return x / t[n]; }
  91. define f(x,n){ /* bitwise leftshift */
  92. scale=0; return (x * t[n]) % z; }
  93. define n(x){ /* bitwise NOT */
  94. return z - 1 - x; }
  95. define a(x,y){ /* bitwise AND */
  96. auto t, i, a, b, r
  97. t = 0; a = x; b = y; r = 1
  98. for (i=0; i<j; ++i) {
  99. t += (a % 2) * (b % 2) * r
  100. r *= 2; a /= 2; b /= 2
  101. }
  102. return t; }
  103. define o(x,y){ /* bitwise OR */
  104. auto t, i, z, w, r, a, b
  105. t = 0; a = x; b = y; r = 1
  106. for (i=0; i<j; ++i) {
  107. z = a % 2; w = b % 2
  108. t += ((z + w + z*w) % 2) * r
  109. r *= 2; a /= 2; b /= 2
  110. }
  111. return t; }
  112. define x(x, y){ /* bitwise XOR */
  113. auto t, i, a, b, r
  114. t = 0; a = x; b = y; r = 1
  115. for (i=0; i<j; ++i) {
  116. t += (((a % 2) + (b % 2)) % 2) * r
  117. r *= 2; a /= 2; b /= 2
  118. }
  119. return t; }
  120. define r(x,n){ /* bitwise rightrotate */
  121. return o(f(x,j-n),g(x,n)); }
  122. /* Extend first 16 words into the remaining 48 words w[16..63] of
  123. the message schedule array: */
  124. for (i=16; i<64; ++i) {
  125. s = x(x(r(w[i-15], 7),r(w[i-15],18)),g(w[i-15], 3))
  126. t = x(x(r(w[i- 2],17),r(w[i- 2],19)),g(w[i- 2],10))
  127. w[i] = p(p(w[i-16],s),p(w[i-7],t))
  128. }
  129. /* Initialize working variables to current hash value */
  130. a=$h0; b=$h1; c=$h2; d=$h3; e=$h4; f=$h5; g=$h6; h=$h7
  131. /* Compression function main loop: */
  132. /* S1 is t
  133. ch is y
  134. temp1 is u
  135. S0 is s
  136. maj is m
  137. temp2 is v
  138. */
  139. for (i=0; i<64; ++i) {
  140. t = x(r(e,6),x(r(e,11),r(e,25)))
  141. y = x(a(e,f),a(n(e),g))
  142. u = p(h,p(t,p(y,p(k[i],w[i]))))
  143. s = x(r(a,2),x(r(a,13),r(a,22)))
  144. m = x(a(a,b),x(a(a,c),a(b,c)))
  145. v = p(s,m)
  146. h = g
  147. g = f
  148. f = e
  149. e = p(d,u)
  150. d = c
  151. c = b
  152. b = a
  153. a = p(u,v)
  154. }
  155. print "h0=", p($h0,a), "\n"
  156. print "h1=", p($h1,b), "\n"
  157. print "h2=", p($h2,c), "\n"
  158. print "h3=", p($h3,d), "\n"
  159. print "h4=", p($h4,e), "\n"
  160. print "h5=", p($h5,f), "\n"
  161. print "h6=", p($h6,g), "\n"
  162. print "h7=", p($h7,h), "\n"
  163. EOF
  164. . $values # Update values for next loop
  165. done
  166. . $values # Import values from loop subprocess
  167. printf "%08x%08x%08x%08x%08x%08x%08x%08x %s\n" \
  168. $h0 $h1 $h2 $h3 $h4 $h5 $h6 $h7 $1
  169. shift # Next input, if available
  170. done