#!/bin/bash


help() {
  cat << EOH

NAME
    $1 - create TLSA resource record data

SYNOPSIS
    $1 [-c CERT-USAGE] [-s SELECTOR] [-m MATCHING-TYPE] [-u] [CERT-FILE]
    $1 -h

DESCRIPTION
    This script creates TLSA resource record data from a given certificate.

OPTIONS
    -c CERT-USAGE
        0: CA constraint / PKIX-TA,
        1: service certificate constraint / PKIX-EE,
        2: trust anchor assertion / DANE-TA,
        3: domain issued certificate / DANE-EE (default).

    -s SELECTOR
        0: select entire certificate for matching,
        1: select public key for certificate matching (default).

    -m MATCHING-TYPE
        0: entire selected data,
        1: SHA-256 hash of selected data (default),
        2: SHA-512 hash of selected data.

    -u
        Output TLSA resource resource data in uppercase.

    -h
        This "manual page".

    CERT-FILE
        Path to the file that contains the certificate in PEM format.
        If CERT-FILE is not given, the chain is expected via stdin.

EXAMPLES
    $ $1 argon.wu6ch.de.cer
    3 1 1 0bcade22628155366833a466f03d7190bc87d1722201d1f449bbe31d76ef52e9

    $ $1 -s 1 -m 2 argon.wu6ch.de.cer
    3 1 2 (
    fc286a1251df50eaddde346ff04838754879aa4075ffe1decb47f1274ca4f208
    e735bfb195e30c0f47c1bcdc21ac175ffc349b914e50549336b2ad36f8b38509 )

    $ echo | openssl s_client -verify_quiet -connect wu6ch.de:443 -showcerts | $1
    3 1 1 0bcade22628155366833a466f03d7190bc87d1722201d1f449bbe31d76ef52e9

    $ echo \\
        | openssl s_client -verify_quiet -connect wu6ch.de:443 -showcerts \\
        | extract-cert-pem -n 2 \\
        | $1 -c 2
    2 1 1 8d02536c887482bc34ff54e41d2ba659bf85b341a0a20afadb5813dcfbcf286d

AUTHOR
    Wolfgang <w6g@wu6ch.de>

LICENSE
    MIT (https://wu6ch.de/bash/functional_bash/LICENSE)

SEE ALSO
    Functional Bash (https://wu6ch.de/bash/functional_bash/)
    extract-cert-pem (https://wu6ch.de/bash/functional_bash/bin/extract-cert-pem)
EOH
}


set_env() {
  source "/usr/local/lib/functional_bash.sh"

  declare -gr openssl="openssl"
  declare -gr hexdump="hexdump"

  # shellcheck disable=SC2034
  local -A options=()
  local -a remaining_args=()

  get_options "c:s:m:u" options remaining_args help "$@" && {
    cert_usage="$(   get_arg options "c" "3")"
    selector="$(     get_arg options "s" "1")"
    matching_type="$(get_arg options "m" "1")"
    uppercase="$(    get_arg options "u"    )"

    set -- "${remaining_args[@]}"
    cert_file="${1-"-"}"

    declare -gr cert_usage selector matching_type uppercase cert_file
  }
}


check_cert_usage() {
  { ((cert_usage >= 0)) && ((cert_usage <= 3)); } \
    || put_err "certificate usage $cert_usage not in range 0..3"
}


check_selector() {
  { ((selector >= 0)) && ((selector <= 1)); } \
    || put_err "selector $selector not in range 0..1"
}


check_matching_type() {
  { ((matching_type >= 0)) && ((matching_type <= 2)); } \
    || put_err "matching type $matching_type not in range 0..2"
}


check_env() {
  check_cert_usage && check_selector && check_matching_type
}


get_pubkey_from_cert() {
  "$openssl" x509 -pubkey -noout 2> /dev/null || put_err "cannot get pubkey from cert"
}


convert_cert_to_der() {
  "$openssl" x509 -outform DER 2> /dev/null || put_err "cannot convert cert to DER"
}


convert_pubkey_to_der() {
  "$openssl" pkey -pubin -outform DER 2> /dev/null || put_err "cannot convert pubkey to DER"
}


hdl_selector() {
  ((selector == 1)) ; eval "$(put_cond "cat" "get_pubkey_from_cert" $?)"
}


convert_to_der() {
  ((selector == 1)) ; eval "$(put_cond "convert_cert_to_der" "convert_pubkey_to_der" $?)"
}


calc_sha() {
  local digest
  ((matching_type == 2)) ; digest="$(put_cond "SHA256" "SHA512" $?)"
  "$openssl" dgst "-${digest}" -binary
}


hdl_mtype() {
  ((matching_type > 0)) ; eval "$(put_cond "cat" "calc_sha" $?)"
}


dump() {
  local format='32/1 "%02x" "\n"'
  is_set "$uppercase" && format="$(tr 'x' 'X' <<< "$format")"

  local rdata_head="$cert_usage $selector $matching_type"
  local hdump ; hdump="$("$hexdump" -v -e "$format")"

  if ((${#hdump} <= 64))
  then
    echo "$rdata_head $hdump"
  else
    echo "$rdata_head ("
    echo "$hdump" | sed -e '$ s/ *$//' -e '$ s/$/ )/'
  fi
}


set_env "$@" \
  && are_installed "$openssl" "$hexdump" \
  && check_env \
  && rdata="$(read_file "$cert_file" | hdl_selector | convert_to_der | hdl_mtype | dump)" \
  && echo "$rdata"


# EOF - vim:cc=101
