| 1 | #!/bin/bash |
| 2 | # Implement HMAC functionality on top of the OpenSSL digest functions. |
| 3 | # licensed under the terms of the GNU GPL v2 |
| 4 | # Copyright 2007 Victor Lowther <victor.lowther@gmail.com> |
| 5 | |
| 6 | die() { |
| 7 | echo $* |
| 8 | exit 1 |
| 9 | } |
| 10 | |
| 11 | check_deps() { |
| 12 | local res=0 |
| 13 | while [ $# -ne 0 ]; do |
| 14 | which "${1}" >& /dev/null || { res=1; echo "${1} not found."; } |
| 15 | shift |
| 16 | done |
| 17 | (( res == 0 )) || die "aborting." |
| 18 | } |
| 19 | |
| 20 | # write a byte (passed as hex) to stdout |
| 21 | write_byte() { |
| 22 | # $1 = byte to write |
| 23 | printf "\\x$(printf "%x" ${1})" |
| 24 | } |
| 25 | |
| 26 | # make an hmac pad out of a key. |
| 27 | # this is not the most secure way of doing it, but it is |
| 28 | # the most expedient. |
| 29 | make_hmac_pad() { |
| 30 | # using key in file $1 and byte in $2, create the appropriate hmac pad |
| 31 | # Pad keys out to $3 bytes |
| 32 | # if key is longer than $3, use hash $4 to hash the key first. |
| 33 | local x y a size remainder oifs |
| 34 | [[ -f ${1} ]] || die "${1} does not exist when making hmac pads." |
| 35 | (( remainder = ${3} )) |
| 36 | # in case someone else was messing with IFS. |
| 37 | for x in $(od -v -t u1 < "${1}"|cut -b 9-); |
| 38 | do |
| 39 | write_byte $((${x} ^ ${2})) |
| 40 | (( remainder -= 1 )) |
| 41 | done |
| 42 | for ((y=0; remainder - y ;y++)); do |
| 43 | write_byte $((0 ^ ${2})) |
| 44 | done |
| 45 | } |
| 46 | |
| 47 | # utility functions for making hmac pads |
| 48 | hmac_ipad() { |
| 49 | make_hmac_pad "${1}" 0x36 ${2} "${3}" |
| 50 | } |
| 51 | |
| 52 | hmac_opad() { |
| 53 | make_hmac_pad "${1}" 0x5c ${2} "${3}" |
| 54 | } |
| 55 | |
| 56 | # hmac something |
| 57 | do_hmac() { |
| 58 | # $1 = algo to use. Must be one that openssl knows about |
| 59 | # $2 = keyfile to use |
| 60 | # $3 = file to hash. uses stdin if none is given. |
| 61 | # accepts input on stdin, leaves it on stdout. |
| 62 | # Output is binary, if you want something else pipe it accordingly. |
| 63 | local blocklen keysize x |
| 64 | case "${1}" in |
| 65 | sha) blocklen=64 ;; |
| 66 | sha1) blocklen=64 ;; |
| 67 | md5) blocklen=64 ;; |
| 68 | md4) blocklen=64 ;; |
| 69 | sha256) blocklen=64 ;; |
| 70 | sha512) blocklen=128 ;; |
| 71 | *) die "Unknown hash ${1} passed to hmac!" ;; |
| 72 | esac |
| 73 | keysize="$(wc -c "${2}")" |
| 74 | (( ${keysize%%[!0-9 ]*} > blocklen )) && \ |
| 75 | die "Prehashing large-size keys not implemented yet. Sorry." |
| 76 | cat <(hmac_ipad ${2} ${blocklen} "${1}") "${3:--}" | openssl dgst "-${1}" -binary | \ |
| 77 | cat <(hmac_opad ${2} ${blocklen} "${1}") - | openssl dgst "-${1}" -binary |
| 78 | } |
| 79 | |
| 80 | [[ ${1} ]] || die "Must pass the name of the hash function to use to ${0}". |
| 81 | |
| 82 | [[ -f ${2} ]] || die "Must pass file containing the secret to $0" |
| 83 | check_deps od openssl |
| 84 | do_hmac "${@}" |