123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182 |
- #!/bin/sh
- # -*- mode: sh; indent-tabs-mode:nil; sh-basic-offset:2 -*-
- #
- # An implementation of the sha256sum using only POSIX shell and
- # POSIX utilities.
- #
- # Requires: sed, nl, xargs, printf, mktemp, stat, od, bc
- #
- # Copyright © 2020 Eric Bavier <bavier@posteo.net>
- # Update: Tue 18 Feb 2020 05:12:46 PM CST
- # Update: Fri 06 Mar 2020 12:15:50 AM CST, rewrite w/ printf, od, and bc
- # Update: Sat 07 Mar 2020 01:22:20 AM CST, speed up bc bitwise operations
- # Update: Fri 03 Apr 2020 06:38:59 PM CDT, fix msg sched init and var shadowing
- # Update: Thu 09 Apr 2020 10:54:39 PM CDT, accept multiple filename arguments
- # Update: Thu 07 Jan 2021 11:37:34 PM CST, small speedup with precomputed 2^n
- # License: GPLv3+
- #####
- # Note 0: Implementation from https://en.wikipedia.org/wiki/SHA-2#Pseudocode
- # Note 1: All variables are 32 bit unsigned integers and addition is
- # calculated modulo 2^32
- # Note 2: For each round, there is one round constant k[i] and one
- # entry in the message schedule array w[i], 0 ≤ i ≤ 63
- # Note 3: The compression function uses 8 working variables, a through h
- # Note 4: Big-endian convention is used when expressing the constants
- # in this pseudocode, and when parsing message block data from
- # bytes to words, for example, the first word of the input
- # message "abc" after padding is 0x61626380
- # Note 5: This implementation is extremely inefficient...
- fn_values_to_array(){
- sed 's/[^ ]\+/0x&/g
- s/ */\n/g' \
- | nl -v0 \
- | xargs printf "${1:0:1}[%d] = %d; "
- }
- fn_quote_hex(){
- sed 's/.*/0000000000000000&/ # left-pad so output is a full 64-bits
- s/.*\(.\{16\}\)/\1/ # trim to 16 nibbles
- :a
- s/^\([^ ]*\)\([^ ][^ ]\)/\1 \\\\x\2/
- t a
- s/^[^ ] /\\\\x& /'
- }
- constants=$(fn_values_to_array "k" <<EOF
- 428a2f98 71374491 b5c0fbcf e9b5dba5 3956c25b 59f111f1 923f82a4 ab1c5ed5
- d807aa98 12835b01 243185be 550c7dc3 72be5d74 80deb1fe 9bdc06a7 c19bf174
- e49b69c1 efbe4786 0fc19dc6 240ca1cc 2de92c6f 4a7484aa 5cb0a9dc 76f988da
- 983e5152 a831c66d b00327c8 bf597fc7 c6e00bf3 d5a79147 06ca6351 14292967
- 27b70a85 2e1b2138 4d2c6dfc 53380d13 650a7354 766a0abb 81c2c92e 92722c85
- a2bfe8a1 a81a664b c24b8b70 c76c51a3 d192e819 d6990624 f40e3585 106aa070
- 19a4c116 1e376c08 2748774c 34b0bcb5 391c0cb3 4ed8aa4a 5b9cca4f 682e6ff3
- 748f82ee 78a5636f 84c87814 8cc70208 90befffa a4506ceb bef9a3f7 c67178f2
- EOF
- )
- values=$(mktemp -p "${TMPDIR:-.}") || exit 1; trap 'rm $values' EXIT
- while test "$1" ; do
- # Initialize hash values:
- # (first 32 bits of the fractional parts of the square roots of the first 8 primes 2..19):
- h0=$(printf "%d" 0x6a09e667)
- h1=$(printf "%d" 0xbb67ae85)
- h2=$(printf "%d" 0x3c6ef372)
- h3=$(printf "%d" 0xa54ff53a)
- h4=$(printf "%d" 0x510e527f)
- h5=$(printf "%d" 0x9b05688c)
- h6=$(printf "%d" 0x1f83d9ab)
- h7=$(printf "%d" 0x5be0cd19)
- size=$(stat --format="%s * 8" "$1" | bc)
- padding=$(printf "t=64-(($size/8)%%64);if(t<9){t+55}else{t-9}\n" | bc)
- (cat "$1";
- printf "\x80";
- printf "%.0s\x00" $(seq 1 $padding);
- printf "%x" $size | fn_quote_hex | xargs printf "%b") \
- | od --address-radix=none --endian=big \
- --format=x4 --width=64 --output-duplicates \
- | while read chunk ; do
- # CHUNK contains 16 64-bit hex values
- message_schedule=$(printf "$chunk\n" | fn_values_to_array "w")
- # Inject arrays into bc for the update step.
- bc >$values <<EOF
- $constants
- $message_schedule
- scale = 0; ibase = A
- j = 32 # bit-width
- z = 2^32
- t[0] = 1 # powers-of-two
- for (i=1; i<=32; ++i)
- t[i] = t[i-1] * 2
- define p(x,y){ /* plus modulo 2^32 */
- scale=0; return (x + y) % z; }
- define g(x,n){ /* bitwise rightshift */
- scale=0; return x / t[n]; }
- define f(x,n){ /* bitwise leftshift */
- scale=0; return (x * t[n]) % z; }
- define n(x){ /* bitwise NOT */
- return z - 1 - x; }
- define a(x,y){ /* bitwise AND */
- auto t, i, a, b, r
- t = 0; a = x; b = y; r = 1
- for (i=0; i<j; ++i) {
- t += (a % 2) * (b % 2) * r
- r *= 2; a /= 2; b /= 2
- }
- return t; }
- define o(x,y){ /* bitwise OR */
- auto t, i, z, w, r, a, b
- t = 0; a = x; b = y; r = 1
- for (i=0; i<j; ++i) {
- z = a % 2; w = b % 2
- t += ((z + w + z*w) % 2) * r
- r *= 2; a /= 2; b /= 2
- }
- return t; }
- define x(x, y){ /* bitwise XOR */
- auto t, i, a, b, r
- t = 0; a = x; b = y; r = 1
- for (i=0; i<j; ++i) {
- t += (((a % 2) + (b % 2)) % 2) * r
- r *= 2; a /= 2; b /= 2
- }
- return t; }
- define r(x,n){ /* bitwise rightrotate */
- return o(f(x,j-n),g(x,n)); }
- /* Extend first 16 words into the remaining 48 words w[16..63] of
- the message schedule array: */
- for (i=16; i<64; ++i) {
- s = x(x(r(w[i-15], 7),r(w[i-15],18)),g(w[i-15], 3))
- t = x(x(r(w[i- 2],17),r(w[i- 2],19)),g(w[i- 2],10))
- w[i] = p(p(w[i-16],s),p(w[i-7],t))
- }
- /* Initialize working variables to current hash value */
- a=$h0; b=$h1; c=$h2; d=$h3; e=$h4; f=$h5; g=$h6; h=$h7
- /* Compression function main loop: */
- /* S1 is t
- ch is y
- temp1 is u
- S0 is s
- maj is m
- temp2 is v
- */
- for (i=0; i<64; ++i) {
- t = x(r(e,6),x(r(e,11),r(e,25)))
- y = x(a(e,f),a(n(e),g))
- u = p(h,p(t,p(y,p(k[i],w[i]))))
- s = x(r(a,2),x(r(a,13),r(a,22)))
- m = x(a(a,b),x(a(a,c),a(b,c)))
- v = p(s,m)
- h = g
- g = f
- f = e
- e = p(d,u)
- d = c
- c = b
- b = a
- a = p(u,v)
- }
- print "h0=", p($h0,a), "\n"
- print "h1=", p($h1,b), "\n"
- print "h2=", p($h2,c), "\n"
- print "h3=", p($h3,d), "\n"
- print "h4=", p($h4,e), "\n"
- print "h5=", p($h5,f), "\n"
- print "h6=", p($h6,g), "\n"
- print "h7=", p($h7,h), "\n"
- EOF
- . $values # Update values for next loop
- done
- . $values # Import values from loop subprocess
- printf "%08x%08x%08x%08x%08x%08x%08x%08x %s\n" \
- $h0 $h1 $h2 $h3 $h4 $h5 $h6 $h7 $1
- shift # Next input, if available
- done
|