---
version: '3'

todo: Collect YUBI_USER_PIN, YUBI_ADMIN_PIN, YUBI_LAST_NAME, YUBI_EMAIL, YUBI_FIRST_NAME, YUBI_TITLE (Principal Software Engineer), YUBI_ALT_NAME, YUBI_ALT_EMAIL, YUBI_ALT_TITLE

vars:
  YUBI_MASTER_EMAIL: no-reply@megabyte.space
  YUBI_MASTER_NAME: Megabyte Labs

env:
  GNUPGHOME:
    sh: echo "$HOME/.gnupghome"
  MASTER_KEY:
    sh: |
      if [ -f .yubi-masterkey ]; then
        cat .yubi-masterkey
      else
        LC_ALL=C tr -dc '[:upper:]' < /dev/urandom | fold -w 30 | head -n1
      fi

tasks:
  add:identities:
    env:
      KEYID: '{{.KEYID}}'
      YUBI_ALT_EMAIL:
        sh: jq -r '.YUBI_ALT_EMAIL' .yubi.json
      YUBI_ALT_NAME:
        sh: jq -r '.YUBI_ALT_NAME' .yubi.json
      YUBI_ALT_TITLE:
        sh: jq -r '.YUBI_ALT_TITLE' .yubi.json
      YUBI_EMAIL:
        sh: jq -r '.YUBI_EMAIL' .yubi.json
      YUBI_FIRST_NAME:
        sh: jq -r '.YUBI_FIRST_NAME' .yubi.json
      YUBI_LAST_NAME:
        sh: jq -r '.YUBI_LAST_NAME' .yubi.json
      YUBI_TITLE:
        sh: jq -r '.YUBI_TITLE' .yubi.json
    cmds:
      - echo -e "${YUBI_FIRST_NAME} ${YUBI_LAST_NAME}\n${YUBI_EMAIL}\n${YUBI_TITLE}\n" | gpg --command-fd 0 --pinentry-mode loopback
        --passphrase "$MASTER_KEY" --expert --edit-key "$KEYID" adduid save
      - echo -e "${YUBI_ALT_NAME}\n${YUBI_ALT_EMAIL}\n${YUBI_ALT_TITLE}\n" | gpg --command-fd 0 --pinentry-mode loopback
        --passphrase "$MASTER_KEY" --expert --edit-key "$KEYID" adduid save

  card:
    vars:
      KEYID:
        sh: gpg --keyid-format long --list-keys {{.YUBI_MASTER_EMAIL}} | grep "pub   rsa" | sed 's/pub   rsa4096\///' | sed 's/^\([^ ]*\).*/\1/'
    env:
      KEYID: '{{.KEYID}}'
      YUBI_ADMIN_PIN:
        sh: jq -r '.YUBI_ADMIN_PIN' .yubi.json
      YUBI_EMAIL:
        sh: jq -r '.YUBI_EMAIL' .yubi.json
      YUBI_FIRST_NAME:
        sh: jq -r '.YUBI_FIRST_NAME' .yubi.json
      YUBI_LAST_NAME:
        sh: jq -r '.YUBI_LAST_NAME' .yubi.json
      YUBI_USER_PIN:
        sh: jq -r '.YUBI_USER_PIN' .yubi.json
    cmds:
      - cp -rf ~/.gnupg ~/.gnupg.bak
      - task: card:reset
      - echo -e "admin\nkdf-setup\n12345678\n" | gpg --command-fd 0 --pinentry-mode loopback --card-edit
      - echo -e "admin\npasswd\n1\n123456\n${YUBI_USER_PIN}\n${YUBI_USER_PIN}\nq\n" | gpg --command-fd 0 --pinentry-mode loopback --card-edit
      - echo -e "admin\npasswd\n3\n12345678\n${YUBI_ADMIN_PIN}\n${YUBI_ADMIN_PIN}\nq\n" | gpg --command-fd 0 --pinentry-mode loopback --card-edit
      - echo -e "admin\nname\n${YUBI_LAST_NAME}\n${YUBI_FIRST_NAME}\n${YUBI_ADMIN_PIN}\nlang\nen\nlogin\n${YUBI_EMAIL}\nquit" | gpg --command-fd 0 --pinentry-mode loopback --card-edit
      - task: card:keys
        vars:
          KEYID: '{{.KEYID}}'
      # - gpg --delete-secret-key "$KEYID"
      # - mv ~/.gnupg ~/.gnupg-generated # TODO
      # - rm -rf ~/.gnupg
      # - mv ~/.gnupg.bak ~/.gnupg

  card:keys:
    env:
      KEYID: '{{.KEYID}}'
      YUBI_ADMIN_PIN:
        sh: jq -r '.YUBI_ADMIN_PIN' .yubi.json
    cmds:
      - echo -e "key 1\nkeytocard\n1\n${MASTER_KEY}\n${YUBI_ADMIN_PIN}\n" | gpg --command-fd 0 --pinentry-mode loopback --edit-key "$KEYID"
      - echo -e "key 2\nkeytocard\n2\n${MASTER_KEY}\n${YUBI_ADMIN_PIN}\n" | gpg --command-fd 0 --pinentry-mode loopback --edit-key "$KEYID"
      - echo -e "key 3\nkeytocard\n3\n${MASTER_KEY}\n${YUBI_ADMIN_PIN}\n" | gpg --command-fd 0 --pinentry-mode loopback --edit-key "$KEYID"

  card:reset:
    cmds:
      - echo -e "admin\nfactory-reset\ny\nyes\n" | gpg --command-fd 0 --pinentry-mode loopback --card-edit
    status:
      - '[ -n "$DONT_RESET_YUBIKEY" ]'

  check:entropy:
    vars:
      ENTROPY_AVAIL:
        sh: |
          if [ -f /proc/sys/kernel/random/entropy_avail ]; then
            cat /proc/sys/kernel/random/entropy_avail
          fi
    cmds:
      - |
        if [ '{{.ENTROPY_AVAIL}}' -lt '2000' ]; then
          .config/log error 'The entropy pool value is not high enough. It must be greater than 2000!' && exit 1
        fi
    status:
      - '[ "{{OS}}" != "linux" ]'

  export:
    vars:
      KEYID:
        sh: gpg --keyid-format long --list-keys {{.YUBI_MASTER_EMAIL}} | grep "pub   rsa" | sed 's/pub   rsa4096\///' | sed 's/^\([^ ]*\).*/\1/'
    cmds:
      - task: public:export
        vars:
          KEYID: '{{.KEYID}}'
      # - task: revocation:export
      #   vars:
      #     KEYID: '{{.KEYID}}'
      - task: secrets:export
        vars:
          KEYID: '{{.KEYID}}'
      - task: public:upload
        vars:
          KEYID: '{{.KEYID}}'
      # - task: usb
      - task: card
      # - task: usb:unmount

  generate:authentication:
    env:
      KEYID: '{{.KEYID}}'
    cmds:
      - echo -e "8\nS\nE\nA\nQ\n4096\n1y\n" | gpg --command-fd 0 --pinentry-mode loopback --passphrase "$MASTER_KEY"
        --expert --edit-key "$KEYID" addkey save
    status:
      - '[ -n "$YUBIKEY_BACKUP" ]'

  generate:encryption:
    env:
      KEYID: '{{.KEYID}}'
    cmds:
      - echo -e "6\n4096\n1y\n" | gpg --command-fd 0 --pinentry-mode loopback --passphrase "$MASTER_KEY" --expert --edit-key "$KEYID" addkey save
    status:
      - '[ -n "$YUBIKEY_BACKUP" ]'

  generate:master:
    cmds:
      - mkdir -p "$GNUPGHOME" && chmod 700 "$GNUPGHOME"
      - mkdir -p "$GNUPGHOME/private-keys-v1.d"
      - chmod 700 "$GNUPGHOME/private-keys-v1.d"
      # Original: echo -e "8\nE\nS\nQ\n4096\n0\ny\n{{.YUBI_MASTER_NAME}}\n{{.YUBI_MASTER_EMAIL}}\n\no\n" -- removed \ny for macOS
      - echo -e "8\nE\nS\nQ\n4096\n0\n{{.YUBI_MASTER_NAME}}\n{{.YUBI_MASTER_EMAIL}}\n\no\n" | gpg --expert --command-fd 0
        --pinentry-mode loopback --passphrase "$MASTER_KEY" --full-generate-key
    status:
      - '[ -n "$YUBIKEY_BACKUP" ]'

  generate:signing:
    env:
      KEYID: '{{.KEYID}}'
    cmds:
      - echo -e "4\n4096\n1y\n" | gpg --command-fd 0 --pinentry-mode loopback --passphrase "$MASTER_KEY" --expert --edit-key "$KEYID" addkey save
    status:
      - '[ -n "$YUBIKEY_BACKUP" ]'

  generate:subkeys:
    vars:
      KEYID:
        sh: gpg --keyid-format long --list-keys {{.YUBI_MASTER_EMAIL}} | grep "pub   rsa" | sed 's/pub   rsa4096\///' | sed 's/^\([^ ]*\).*/\1/'
    cmds:
      - task: generate:signing
        vars:
          KEYID: '{{.KEYID}}'
      - task: generate:encryption
        vars:
          KEYID: '{{.KEYID}}'
      - task: generate:authentication
        vars:
          KEYID: '{{.KEYID}}'
      - task: add:identities
        vars:
          KEYID: '{{.KEYID}}'

  prepare:
    desc: Prepares an environment by provisioning all the required software / preliminary steps
    cmds:
      - mkdir -p "$GNUPGHOME" && chmod 700 "$GNUPGHOME"
      - echo '{{.MASTER_KEY}}' > .yubi-masterkey
      - task: prepare:init
      - task: :install:service:start
        vars:
          SERVICE: pcscd
      - task: check:entropy
      - task: generate:master
      - task: generate:subkeys
        vars:
          KEYID:
            sh: gpg --keyid-format long --list-keys {{.YUBI_MASTER_EMAIL}} | grep "pub   rsa" | sed 's/pub   rsa4096\///' | sed 's/^\([^ ]*\).*/\1/'
      - task: export
      - task: :security:ssh:yubikey

  prepare:init:
    cmds:
      - task: prepare:init:continue
    status:
      - '[ -n "$YUBIKEY_BACKUP" ]'

  prepare:init:continue:
    deps:
      - :install:requirements:yubikey
      - :security:gpg:conf
      - task: :security:gpg:conf
        vars:
          CONFIG_DIR_PATH:
            sh: echo "$GNUPGHOME"
      - :security:gpg:conf:agent
      - task: :security:gpg:conf:agent
        vars:
          CONFIG_DIR_PATH:
            sh: echo "$GNUPGHOME"

  public:export:
    todo: This differs from guide
    env:
      KEYID: '{{.KEYID}}'
    cmds:
      - mkdir -p "$GNUPGHOME" && chmod 700 "$GNUPGHOME"
      - gpg --armor --export "$KEYID" > "$GNUPGHOME/gpg-$KEYID-$(date +%F).asc"
    status:
      - '[ -n "$YUBIKEY_BACKUP" ]'

  public:upload:
    cmds:
      - gpg --send-key "$KEYID"
      - gpg --keyserver pgp.mit.edu --send-key "$KEYID"
      - gpg --keyserver keys.gnupg.net --send-key "$KEYID"
      - gpg --keyserver hkps://keyserver.ubuntu.com:443 --send-key "$KEYID"
    status:
      - |
        [ "$(echo -e "GET http://google.com HTTP/1.0\n\n" | nc google.com 80 > /dev/null 2>&1)" != "0" ] || [ -n "$YUBIKEY_BACKUP" ]

  revocation:export:
    env:
      KEYID: '{{.KEYID}}'
    cmds:
      - mkdir -p "$GNUPGHOME" && chmod 700 "$GNUPGHOME"
      - gpg --output $GNUPGHOME/revoke.asc --gen-revoke "$KEYID"
    status:
      - '[ -n "$YUBIKEY_BACKUP" ]'

  secrets:export:
    env:
      KEYID: '{{.KEYID}}'
    cmds:
      - mkdir -p "$GNUPGHOME" && chmod 700 "$GNUPGHOME"
      - gpg --armor --export-secret-keys --command-fd 0 --pinentry-mode loopback
        --passphrase "$MASTER_KEY" --expert "$KEYID" > $GNUPGHOME/mastersub.key
      - gpg --armor --export-secret-subkeys --command-fd 0 --pinentry-mode loopback
        --passphrase "$MASTER_KEY" --expert "$KEYID" > $GNUPGHOME/sub.key
    status:
      - '[ -n "$YUBIKEY_BACKUP" ]'

  secure:delete:
    env:
      KEYID: '{{.KEYID}}'
    cmds:
      - sudo srm -rf "$GNUPGHOME" || sudo rm -rf "$GNUPGHOME"
      - gpg --delete-secret-key "$KEYID"

  stub:
    summary: |
      Run this to re-stub the GPG files to point to the current YubiKey.
    cmds:
      - gpg-connect-agent "scd serialno" "learn --force" /bye

  usb:
    summary: |
      # Backup Keys (Including Master) to USB and Encrypt

      This task will partition a USB with two partitions. One partition will
      be encrypted and contain your GPG keys (including the master key). The other
      partition will not be encrypted and contain your public key.
    cmds:
      - task: usb:create
      - task: usb:mount
      - mkdir ~/.gpg-encrypted-storage && cp -rf $GNUPGHOME ~/.gpg-encrypted-storage # TODO /mnt/gpg-encrypted-storage
      - mkdir ~/.gpg-public && cp $GNUPGHOME/gpg-$KEYID* ~/.gpg-public # TODO /mnt/gpg-public
    status:
      - '[ -n "$YUBIKEY_BACKUP" ]'

  usb:create:
    deps:
      - :security:disk:encrypt:create
      - :security:disk:unencrypted:create

  usb:mount:
    deps:
      - :security:disk:encrypt:mount
      - :security:disk:unencrypted:mount

  usb:unmount:
    deps:
      - :security:disk:encrypt:unmount
      - :security:disk:unencrypted:unmount
    status:
      - '[ -n "$YUBIKEY_BACKUP" ]'

  yubikey-agent:
    deps:
      - :install:software:yubikey-agent
    summary: |
      # Sets Up Resident SSH Key

      According to [this guide](https://github.com/jamesog/yubikey-ssh), the yubikey-agent
      package is a simplest, best way of utilizing a YubiKey for SSH by leveraging
      YubiKey's onboard SSH key generator. By using this method, the private key never leaves
      the YubiKey which increases security tremendously.

      See: https://github.com/FiloSottile/yubikey-agent

      A new method is detailed here: https://www.yubico.com/blog/github-now-supports-ssh-security-keys/
      It may be worth migrating to that approach.
    cmds:
      - brew services start yubikey-agent
      - yubikey-agent -setup
