#!/usr/bin/env sh
# =============================================================================
#  軽量ファイルの暗号化スクリプト (UTF-8)
# =============================================================================
#
# GitHub 上で公開されている（https://github.com/<user name>.keys で取得できる）
# 相手の RSA 公開鍵を使って小さなファイルを暗号化します。
#
# - 使い方の例：
#   以下のコマンドで暗号化された'himitsu.txt.enc' が作成されます。
#       $ ./enc.sh KEINOS ./himitsu.txt
#
# - 注意：利用前にスクリプトに実行権限を与えるのを忘れないでください。

# -----------------------------------------------------------------------------
#  Requirement check
# -----------------------------------------------------------------------------
if ! type openssl 2>/dev/null 1>/dev/null; then
    echo >&2 '暗号化に必要な openssl コマンドがインストールされていません。'

    exit 1
fi
if ! type ssh-keygen 2>/dev/null 1>/dev/null; then
    echo >&2 'ssh-keygen コマンド(openssh)がインストールされていません。'

    exit 1
fi

# -----------------------------------------------------------------------------
#  Main
# -----------------------------------------------------------------------------

# ヘルプ表示
# ----------------
if [ $# -lt 2 ]; then
    echo
    echo "使い方: $0 <github user> <input file> [<output file>]"
    echo
    echo "- <github user> : 相手の GitHub アカウント名"
    echo "- <input file>  : 暗号化したいファイルのパス"
    echo "[オプション]"
    echo "- <output file> : 暗号化されたファイルの出力先のパス"
    echo "                  指定がない場合は <input_file>.enc として出力されます。"
    echo "[注意]"
    echo "- このスクリプトは 1 ブロックぶんのデータのみ暗号化できます。"
    echo
    exit 1
fi

getRandStr() {
    openssl rand -hex 16 2>&1
}

catURL() {
    if type curl 1>/dev/null 2>/dev/null; then
        curl --fail -s "$1"
    elif type wget 1>/dev/null 2>/dev/null; then
        wget -nv -O - "$1"
    elif type fetch 1>/dev/null 2>/dev/null; then
        fetch -q -o - "$1"
    else
        echo >&2 'データ取得に必要なコマンド(curl/wget/fetch)がインストールされていません。'
        exit 1
    fi
}

# コマンド引数取得
# ----------------
USERNAME="$1"
INPUTFILE="$2"

if [ ! -r "$INPUTFILE" ]; then
    echo >&2 "暗号化したいファイル ${INPUTFILE} が見つかりません。"
    exit 1
fi

# 出力ファイル名設定
# ------------------
OUTPUTFILE="$2.enc"
if [ $# -eq 3 ]; then
    OUTPUTFILE=$3
fi

# trap の設定
# -----------
# スクリプト終了後一時ファイルを削除します。
# - 参考URL ： https://qiita.com/m-yamashita/items/889c116b92dc0bf4ea7d
trap 'rm -rf /tmp/${USERNAME}.*' 0

# 一時ファイル
# ------------
if ! RAND="$(getRandStr)"; then
    echo >&2 'OpenSSL によるランダム値の取得に失敗しました。'
    echo >&2 "エラー内容: ${RAND}"
    exit 1
fi
PATHPUBKEY="/tmp/${USERNAME}.${RAND}.pub"

# RSA 公開鍵の取得
# ----------------
# ユーザの GitHub の公開鍵一覧の１行目を取得
# - 取得先は： https://github.com/<user name>.keys
# - 参考URL ： https://qiita.com/m0r1/items/af16c41475d493ab6774
printf "%s" "${USERNAME} の GitHub 上の公開鍵を取得中 ... "

list_keys="$(catURL "https://github.com/${USERNAME}.keys")" || {
    echo >&2 "NG：公開鍵を取得できませんでした。"
    exit 1
}
if [ -z "$list_keys" ]; then
    echo >&2 "NG：公開鍵が存在しませんでした"
    exit 1
fi
echo "$list_keys" | head -n 1 >"$PATHPUBKEY" || {
    echo >&2 "NG：公開鍵を保存できませんでした。"
    exit 1
}
echo "OK"

# 公開鍵のアクセス権変更
# ------------------
printf "%s" "取得した公開鍵のアクセス権を変更中 (0644 -> 0600) ... "

if ! chmod 0600 "$PATHPUBKEY"; then
    echo >&2 "NG：ファイルのアクセス権を変更できませんでした。"
    exit 1
fi
echo "OK"

# 公開鍵のフォーマット変換
# ------------------------
# - 参考URL ：
#   - https://qiita.com/drobune/items/bf5d689eff7f69ed6866
#   - https://qiita.com/connvoi_tyou/items/3e86b6b68c3f398b3244
printf "%s" "RSA 形式の公開鍵を PKCS8 形式に変換中 ... "

if ! ssh-keygen -f "$PATHPUBKEY" -e -m pkcs8 >"${PATHPUBKEY}.pkcs8"; then
    echo >&2 "NG：RSA -> PKCS8 変換中にエラーが発生しました。"
    exit 1
fi
echo "OK"

# テキストの暗号化
# ----------------
# このスクリプトは 1 ブロックで可能なデータ量のみ暗号化します。そのため長いテキ
# ストは暗号化できません。
# - 参考URL ： https://qiita.com/kunichiko/items/3c0b1a2915e9dacbd4c1
# - RSA鍵のビット長 = 最大暗号化可能バイト数
#        768 =   85
#       1024 =  117
#       2048 =  246
#       4096 =  502
#       8192 = 1018
printf "%s" "公開鍵でファイルを暗号化中 ... "

if ! openssl rsautl \
    -encrypt \
    -pubin \
    -inkey "${PATHPUBKEY}.pkcs8" \
    -in "$INPUTFILE" \
    -out "$OUTPUTFILE"; then
    echo >&2 "NG：暗号化に失敗しました。ファイルのサイズなど、エラー内容を確認ください。"
    exit 1
fi
echo "OK"

# 一時ファイルの削除
# ------------------
printf "%s" "一時ファイルの削除中 ... "

if ! (rm "$PATHPUBKEY" && rm "${PATHPUBKEY}.pkcs8"); then
    echo >&2 "NG：一時ファイルの削除に失敗しました。 '/tmp/' ディレクトリ内を手動で削除してください。"
    exit 1
fi
echo "OK"

# 終了表示
# --------
echo
echo "暗号化を完了しました。このファイルを相手に送ってください。"
echo "暗号化済みファイル： ${OUTPUTFILE}"
echo
exit 0
