#!/bin/bash
set -eo pipefail

: '
-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA256

'

DN42CA_PKCS12=${DN42CA_PKCS12-"0"}

VERSION="0.3.9"
BASE_URL=${DN42CA_BASEURL-"https://ca.dn42"}
GPG_KEY="$BASE_URL/ca.dn42.key"

CURL=$(command -v curl)

# Some versions of openssl don't like the name constraints we use. We need to ignore cert errors for them.
CURL="$CURL -k -s"

CUR_VERSION=$($CURL "$BASE_URL/ca.dn42.version")

ver() { printf "%03d%03d%03d%03d" $(echo "$1" | tr '.' ' ') 0 0 0; }

check_version() {
  if [ "$(ver "$VERSION")" -lt "$(ver "$CUR_VERSION")" ]; then
    echo "Current script version is $CUR_VERSION. You are running $VERSION"
    echo "Get it here: https://ca.dn42/ca.dn42"
    exit 1
  fi
}

usage() {
  cat 2>&1 <<EOL
Usage:  # OWNER is your MNT handle.
   $0 user-gen OWNER EMAIL           # Output to OWNER.csr and OWNER.key
   $0 user-sign OWNER                # Output to OWNER.crt and OWNER.p12
   $0 tls-gen DNS OWNER EMAIL [SAN]  # Output to OWNER_DNS.csr and OWNER.key
   $0 tls-sign DNS OWNER             # Output to OWNER_DNS.crt and OWNER_DNS.p12
   $0 revoke OWNER CERTFILE [REASON] # Reasons: unspecified, keyCompromise, affiliationChanged,
                                     # superseded, cessationOfOperation, certificateHold, removeFromCRL
   $0 reg-edit OWNER TYPE OBJECT     # Registry editor
   $0 check-gpg                      # Check GPG signature of script
   $0 update                         # Update script. (requires write access to $0)

Environtment Options:
   DN42CA_PKCS12 = 1                # Generate pkcs12 file for certificate.
   DN42CA_NEWKEY = 1                # Do not create a new key if one exists.
EOL
  exit 1
}

CMD=${1-"NONE"}; shift

KEYOUT="-key"

case $CMD in

  "user-gen")
    check_version

    OWNER=${1-"DUMMY-MNT"}
    EMAIL=${2-"dn42@dummy.tld"}

    if [ "$DN42CA_NEWKEY" = "1" ] || [ ! -f "${OWNER}.key" ]; then
      KEYOUT="-keyout"
    else
      echo "Reusing key: ${OWNER}.key"
    fi

    openssl req -new \
       -subj "/C=XD/O=dn42/OU=dn42 Certificate Authority/CN=${OWNER}/emailAddress=${EMAIL}/owner=${OWNER}" \
       -out "${OWNER}.csr" \
       "$KEYOUT" "${OWNER}.key" \
       -config <(cat <<EOL
[req]
  default_bits = 2048
  encrypt_key = no
  default_md = sha256
  utf8 = yes
  string_mask = utf8only
  prompt = yes
  distinguished_name = client_dn
  req_extensions = client_reqext
[client_dn]
[client_reqext]
  keyUsage = critical,digitalSignature
  extendedKeyUsage = clientAuth
  subjectKeyIdentifier = hash
EOL
)

    echo "="
    echo "= You need to have this pin added to your mnt object before proceeding to the next step."
    echo "="
    echo -n "|MNT Key Pin| remarks: pin-sha256:"
      (openssl req -in "${OWNER}.csr" -pubkey -noout | \
       openssl rsa -pubin -outform der | \
       openssl dgst -sha256 -binary | \
       openssl enc -base64) 2> /dev/null

    exit 0
  ;;

  user-sign)
    check_version

    OWNER=${1-"DUMMY-MNT"}
    STATUS=$(mktemp)

    $CURL -X POST \
         -H "Content-Type: text/plain" \
         --data-binary @"${OWNER}.csr" \
         "$BASE_URL/user.php" > "$STATUS"

    cat "$STATUS"; echo
    CERT_OK=$(grep OK "$STATUS" | cut -c1-2)

    if [ "$CERT_OK" = "OK" ]; then
      $CURL "$(grep OK "$STATUS" | cut -d' ' -f2)" > "${OWNER}.crt"
      $CURL "$BASE_URL/crt/ca-chain.crt" > ca-chain.crt

      if [ "$DN42CA_PKCS12" -eq "1" ]; then
        echo "="
        echo "= Create a pkcs12 bundle"
        echo "="
        openssl pkcs12 -export \
          -name "${OWNER} (ca.dn42 mnter)" \
          -caname "dn42 Internal CA (VERIFIED)" \
          -caname "dn42 Root Authority CA" \
          -inkey "${OWNER}.key" \
          -in "${OWNER}.crt" \
          -certfile ca-chain.crt \
          -out "${OWNER}.p12"
       fi

       rm "$STATUS"
       exit 0
    fi

    rm "$STATUS"
    exit 1
  ;;

  tls-gen)
    check_version

    DNS=${1-"dummy.dn42"}
    OWNER=${2-"DUMMY-MNT"}
    EMAIL=${3-"dn42@dummy.tld"}
    SAN=$4

    FILE="${OWNER}_${DNS}"
    if [ "$DN42CA_NEWKEY" = "1" ] || [ ! -f "${FILE}.key" ]; then
      KEYOUT="-keyout"
    else
      echo "Reusing key: ${FILE}.key"
    fi

    SANCONFIG=""
    if [ ! -z "$SAN" ]; then
       SANCONFIG="subjectAltName = \$ENV::SAN"
    fi

    SAN=$SAN \
    openssl req -new \
        -subj "/C=XD/O=dn42/OU=dn42 Certificate Authority/CN=${DNS}/emailAddress=${EMAIL}/owner=${OWNER}" \
        -out "${FILE}.csr" \
        "$KEYOUT" "${FILE}.key" \
        -config <(cat <<EOL
[default]
SAN = DNS:yourdomain.dn42
[req]
default_bits = 2048
encrypt_key = no
default_md = sha256
utf8 = yes
string_mask = utf8only
prompt = yes
distinguished_name = server_dn
req_extensions = server_reqext
[server_dn]
[server_reqext]
keyUsage = critical,digitalSignature,keyEncipherment
extendedKeyUsage = serverAuth,clientAuth
subjectKeyIdentifier = hash
$SANCONFIG
EOL
)

    KEYPIN=$(openssl req -in "${FILE}.csr" -pubkey -noout | \
             openssl rsa -pubin -outform der | \
             openssl dgst -sha256 -binary | \
             openssl enc -base64 2> /dev/null)

    echo "="
    echo "= |DNS Key Pin| You need to have this pin added to your dns records  before proceeding to the next step."
    echo "="

    (echo "_dn42_tlsverify.${DNS}. IN TXT ${OWNER}:pin-sha256:${KEYPIN}";
      for i in ${SAN//,/ }; do echo "_dn42_tlsverify.${i//DNS:/}. IN TXT ${OWNER}:pin-sha256:${KEYPIN}"; done) | sort | uniq

  ;;

  tls-sign)
    check_version

    DNS=${1-"dummy.dn42"}
    OWNER=${2-"DUMMY-MNT"}
    STATUS=$(mktemp)

    FILE="${OWNER}_${DNS}"

    $CURL -X POST \
         --cert "./${OWNER}.crt" \
         --key "./$OWNER.key" \
         -H "Content-Type: text/plain" \
         --data-binary @"${FILE}.csr" \
         "$BASE_URL/server.php" > "$STATUS"

    cat "$STATUS"; echo
    CERT_OK=$(grep OK "$STATUS" | cut -c1-2)

    if [ "$CERT_OK" = "OK" ]; then
      $CURL "$(grep OK "$STATUS" | cut -d' ' -f2)" > "${FILE}.crt"
      $CURL "$BASE_URL/crt/ca-chain.crt" > ca-chain.crt

      sed -n "/BEGIN/,/END/p;/END/q" < ca-chain.crt >> "${FILE}.crt"

      if [ "$DN42CA_PKCS12" -eq "1" ]; then
        echo "="
        echo "= Create a pkcs12 bundle"
        echo "="
        openssl pkcs12 -export \
          -name "${DNS} (${OWNER} from ca.dn42 tls)" \
          -caname "dn42 Internal CA (VERIFIED)" \
          -caname "dn42 Root Authority CA" \
          -inkey "${FILE}.key" \
          -in "${FILE}.crt" \
          -certfile ca-chain.crt \
          -out "${FILE}.p12"
      fi

      rm "$STATUS"
      exit 0
    fi

    echo "Run Failed"
    cat "$STATUS"
    rm "$STATUS"
    exit 1
  ;;

  revoke)
    check_version

    OWNER=${1-"DUMMY-MNT"}
    FILE=${2-"filename.crt"}
    REASON=${3-"unspecified"}
    STATUS=$(mktemp)
    SERIAL=$(openssl x509 -serial -noout -in "${FILE}" | cut -d'=' -f2)
    $CURL -X POST \
         --cert "./${OWNER}.crt" \
         --key "./$OWNER.key" \
         -H "Content-Type: text/plain" \
         "$BASE_URL/revoke.php?serial=${SERIAL}&reason=${REASON}" > "$STATUS"

    cat "$STATUS"; echo
    CERT_OK=$(grep OK "$STATUS" | cut -c1-2)
    if [ "$CERT_OK" = "OK" ]; then
      exit 0
    fi

    rm "$STATUS"
    exit 1
  ;;

  reg-edit)
    check_version

    OWNER=${1-"DUMMY-MNT"}
    TYPE=${2-"person"}
    OBJECT=${3-"DUMMY-DN42"}
    REG_EDIT=${VISUAL-${EDITOR-"vi"}}
    REG_ORIG=$(mktemp)
    REG_CHNG=$(mktemp)
    STATUS=$(mktemp)

    echo "GET: $BASE_URL/reg/$TYPE/$OBJECT"
    echo "Make desired changes and save to submit. Do not save changes to abort."

    $CURL -X GET \
          "$BASE_URL/reg/$TYPE/$OBJECT" > "$REG_ORIG"
    cp "$REG_ORIG" "$REG_CHNG"

    $REG_EDIT "$REG_CHNG"

    if cmp "$REG_ORIG" "$REG_CHNG" >/dev/null 2>&1
    then
      echo "No change detected. Aborting."
      rm "$REG_ORIG" "$REG_CHNG" "$STATUS"
      exit 1
    fi

    echo "Posting updates to server."
    $CURL -X PUT \
          --cert "./$OWNER.crt" \
          --key "./$OWNER.key" \
          -H "Content-Type: text/plain" \
          --data-binary @"$REG_CHNG" \
        "$BASE_URL/reg.php/$TYPE/$OBJECT" > "$STATUS"

    cat "$STATUS"; echo
    CERT_OK=$(grep OK "$STATUS" | cut -c1-2)
    if [ "$CERT_OK" = "OK" ]; then
      rm "$REG_ORIG" "$REG_CHNG" "$STATUS"
      exit 0
    fi

    rm "$REG_ORIG" "$REG_CHNG" "$STATUS"
  ;;

  check-gpg)
    check_version

    $CURL "$GPG_KEY" | gpg --import
    gpg --verify "$0"
  ;;

  update)
    if [ "$(ver "$VERSION")" -lt "$(ver "$CUR_VERSION")" ] && [ -w "$0" ]
    then
      TMP=$(mktemp)
      $CURL "$BASE_URL/ca.dn42" > "$TMP"
      bash "$TMP" check-gpg || exit 1
      cat "$TMP" > "$0"
      rm "$TMP"
    fi
  ;;

  *)
    usage
  ;;

esac

exit 0

:'
-----BEGIN PGP SIGNATURE-----

iQIzBAEBCAAdFiEEIK4vMQp06nzsOuafizsGBPFk4E8FAltD7LUACgkQizsGBPFk
4E8nNRAAh9iA1OHf9EahUzLEn/Q7HdSiFbNv39iOC7oqdn5RRxAL2Q1wf/s4K6rf
6PQ2NFu4eFMIsjnzMOELwuWW+thZ9VHuGFPQaxy/c3nWazZRTlc5yo8TgpAadMOV
7AbtqoGWSenaJVbEfpUYftS1cyvsjSBI8wJiyxlK09hdfsbY3vXMVqWjyRan6e7C
MGRtBQAI8ulkGxWWmcAZmyRD2kgpYS3AIs3wVCu3iwPEHiVelsvV7ppkMhuHCM1E
/vS7Zl6BvJHZpaw+BpD39q3MFc3rIUmhL9D4ZTFFsuIXf9m0/YpOtAOoFJ4dgb3j
r6c86/m16CRtEXJKPhc0Oyk/qF9KCXzebJch7Zus5VE2zEvJGUOXojiIXA08reh/
lZPQTjXEbHsH8NbPd6XBXjS6U0EgOH1FwvEbnCdki1+mwrdX7cNiJQmu/E3l2n5r
9c239+NpVWsowyNlGh00Pd4wqPMVuge+eVmgKcQavUbeDs2A1NKCXXUz0xwLSun9
vuUHrlpvVbtEm9Kr7n6PUD1OHMN4rqngpxMLtj+7xsje2zhXULqE19IxCePbNhLk
qhJzLw5l+6rU5/alYrZjlZ7IavK1ertZlokmcrepoTUAHkb/Fp+cB83JA50Rdoc4
bUjrwsdxy1ruYCSiWNdDle8I4zlLxNe6h/aSZNwubhl1IacRWB4=
=NzIs
-----END PGP SIGNATURE-----
'