2026-01-12 10:34:04 +00:00
|
|
|
|
#!/bin/bash
|
|
|
|
|
|
set -euo pipefail
|
|
|
|
|
|
|
2026-01-20 16:40:01 +09:00
|
|
|
|
# nbcrypt - SSH Agent ベースの暗号化/復号ツール
|
|
|
|
|
|
# Ed25519 署名で決定的な鍵導出を行う
|
2026-01-21 01:17:52 +00:00
|
|
|
|
#
|
2026-01-22 01:57:07 +00:00
|
|
|
|
# 重要: 単体動作を最優先する原則
|
|
|
|
|
|
# - このスクリプトは extras/bootstrap の仕組みの中で単体処理として使われる
|
|
|
|
|
|
# - nbconf などの他の nbase2 ツールへの依存は避ける
|
|
|
|
|
|
# - 環境変数や kernel keyring などの標準的な仕組みのみを使用する
|
2026-01-12 10:34:04 +00:00
|
|
|
|
|
|
|
|
|
|
SCRIPT_NAME="$(basename "$0")"
|
|
|
|
|
|
KEY_SEED_MESSAGE="nbase2-secret-key-seed-v1"
|
|
|
|
|
|
KEY_IDENTITY="id_ed25519"
|
|
|
|
|
|
|
2026-01-20 16:40:01 +09:00
|
|
|
|
# 出力用の色
|
2026-01-12 10:34:04 +00:00
|
|
|
|
RED='\033[0;31m'
|
|
|
|
|
|
GREEN='\033[0;32m'
|
|
|
|
|
|
YELLOW='\033[1;33m'
|
2026-01-20 16:40:01 +09:00
|
|
|
|
NC='\033[0m' # リセット
|
2026-01-12 10:34:04 +00:00
|
|
|
|
|
|
|
|
|
|
error() {
|
|
|
|
|
|
echo -e "${RED}❌ Error: $*${NC}" >&2
|
|
|
|
|
|
exit 1
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
info() {
|
|
|
|
|
|
echo -e "${GREEN}ℹ️ $*${NC}" >&2
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
warn() {
|
|
|
|
|
|
echo -e "${YELLOW}⚠️ $*${NC}" >&2
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-01-20 16:40:01 +09:00
|
|
|
|
print_source_agent_hint() {
|
|
|
|
|
|
local env_file="/tmp/.nb_agent_env_${USER:-$(id -un)}"
|
|
|
|
|
|
echo "" >&2
|
|
|
|
|
|
info "この agent を現在のシェルで使うには、次を実行:"
|
|
|
|
|
|
echo " source $env_file" >&2
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-01-12 10:34:04 +00:00
|
|
|
|
usage() {
|
|
|
|
|
|
cat <<EOF
|
|
|
|
|
|
Usage: $SCRIPT_NAME <command> [arguments]
|
|
|
|
|
|
|
|
|
|
|
|
Commands:
|
2026-01-20 16:40:01 +09:00
|
|
|
|
enc <text> Encrypt a text string (or use enctext)
|
|
|
|
|
|
dec <text> Decrypt a text string (or use dectext)
|
|
|
|
|
|
enctext <text> Encrypt a text string (alias for enc)
|
|
|
|
|
|
dectext <text> Decrypt a text string (alias for dec)
|
|
|
|
|
|
encfile <input> <output> Encrypt a file (or use encrypt)
|
|
|
|
|
|
decfile <input> <output> Decrypt a file (or use decrypt)
|
|
|
|
|
|
encrypt <input> <output> Encrypt a file (alias for encfile)
|
|
|
|
|
|
decrypt [options] <input> <output> Decrypt a file (alias for decfile)
|
|
|
|
|
|
keychain [-k|--keep] Fetch SSH keys from BWS and run ssh-add.
|
|
|
|
|
|
Default: remove key files after ssh-add. Use -k/--keep to keep.
|
2026-01-18 07:52:01 +00:00
|
|
|
|
install-bws Install Bitwarden Secrets Manager CLI (bws)
|
2026-01-12 10:34:04 +00:00
|
|
|
|
check Check if SSH Agent has required key
|
|
|
|
|
|
help Show this help message
|
|
|
|
|
|
|
2026-01-20 16:40:01 +09:00
|
|
|
|
Options for decrypt/decfile:
|
2026-01-18 08:15:10 +00:00
|
|
|
|
-f, --force Force download nbloader from BWS and setup SSH Agent
|
|
|
|
|
|
(skips SSH Agent check, requires BWS_ACCESS_TOKEN)
|
|
|
|
|
|
|
2026-01-20 16:40:01 +09:00
|
|
|
|
Text commands support stdin:
|
|
|
|
|
|
echo "secret" | $SCRIPT_NAME enc -
|
|
|
|
|
|
echo "encrypted" | $SCRIPT_NAME dec -
|
|
|
|
|
|
|
2026-01-12 10:34:04 +00:00
|
|
|
|
Requirements:
|
2026-01-22 01:57:07 +00:00
|
|
|
|
- SSH Agent with id_ed25519 key, or ~/.ssh/id_ed25519 file, or BWS_ACCESS_TOKEN
|
2026-01-12 10:34:04 +00:00
|
|
|
|
- ssh-keygen and openssl commands must be available
|
|
|
|
|
|
|
|
|
|
|
|
Examples:
|
2026-01-20 16:40:01 +09:00
|
|
|
|
$SCRIPT_NAME enc "secret text"
|
|
|
|
|
|
$SCRIPT_NAME dec "ENC:U2FsdGVkX1..."
|
|
|
|
|
|
$SCRIPT_NAME encfile secrets.txt secrets.enc
|
|
|
|
|
|
$SCRIPT_NAME decfile secrets.enc secrets.txt
|
|
|
|
|
|
$SCRIPT_NAME keychain
|
2026-01-18 07:52:01 +00:00
|
|
|
|
$SCRIPT_NAME install-bws
|
2026-01-12 10:34:04 +00:00
|
|
|
|
$SCRIPT_NAME check
|
|
|
|
|
|
|
|
|
|
|
|
EOF
|
|
|
|
|
|
exit 0
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
check_dependencies() {
|
|
|
|
|
|
local missing=()
|
|
|
|
|
|
|
|
|
|
|
|
for cmd in ssh-keygen openssl ssh-add; do
|
|
|
|
|
|
if ! command -v "$cmd" >/dev/null 2>&1; then
|
|
|
|
|
|
missing+=("$cmd")
|
|
|
|
|
|
fi
|
|
|
|
|
|
done
|
|
|
|
|
|
|
|
|
|
|
|
if [ ${#missing[@]} -gt 0 ]; then
|
|
|
|
|
|
error "Missing required commands: ${missing[*]}"
|
|
|
|
|
|
fi
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-01-20 16:40:01 +09:00
|
|
|
|
get_bws_token() {
|
2026-01-22 01:57:07 +00:00
|
|
|
|
# 1. nbconf が利用可能な場合、nbconf から取得を試みる(keyring キャッシュも含む)
|
2026-01-20 16:40:01 +09:00
|
|
|
|
local nbase_root="${NBASE_HOME:-$(cd "$(dirname "$0")/.." && pwd)}"
|
|
|
|
|
|
local nbconf_cmd="${nbase_root}/bin/nbconf"
|
|
|
|
|
|
if [ -f "$nbconf_cmd" ] && [ -x "$nbconf_cmd" ]; then
|
|
|
|
|
|
local val
|
|
|
|
|
|
val=$("$nbconf_cmd" get BWS_ACCESS_TOKEN -d 2>/dev/null) || true
|
|
|
|
|
|
if [ -n "$val" ]; then
|
|
|
|
|
|
echo "$val"
|
|
|
|
|
|
return 0
|
|
|
|
|
|
fi
|
|
|
|
|
|
fi
|
2026-01-22 01:57:07 +00:00
|
|
|
|
|
|
|
|
|
|
# 2. すでに環境変数にある
|
|
|
|
|
|
if [ -n "${BWS_ACCESS_TOKEN:-}" ]; then
|
|
|
|
|
|
echo "$BWS_ACCESS_TOKEN"
|
|
|
|
|
|
return 0
|
|
|
|
|
|
fi
|
|
|
|
|
|
|
2026-01-20 16:40:01 +09:00
|
|
|
|
return 1
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-01-12 10:34:04 +00:00
|
|
|
|
check_ssh_agent() {
|
2026-01-20 16:40:01 +09:00
|
|
|
|
local env_file="/tmp/.nb_agent_env_${USER:-$(id -un)}"
|
|
|
|
|
|
|
|
|
|
|
|
# まず、以前の BWS セットアップで作られた env ファイルがあれば読む
|
|
|
|
|
|
# SSH_AUTH_SOCK の検査より前にやる(env にソケットパスが入っているため)
|
|
|
|
|
|
if [ -f "$env_file" ]; then
|
|
|
|
|
|
source "$env_file"
|
|
|
|
|
|
info "SSH Agent environment loaded from $env_file"
|
|
|
|
|
|
|
|
|
|
|
|
# 読んだあと ssh-add が動くか確認
|
|
|
|
|
|
if [ -n "${SSH_AUTH_SOCK:-}" ] && [ -S "${SSH_AUTH_SOCK}" ]; then
|
|
|
|
|
|
if ssh-add -l >/dev/null 2>&1; then
|
|
|
|
|
|
# Ed25519 が載っているか確認
|
|
|
|
|
|
if ssh-add -l 2>/dev/null | grep -q "ED25519"; then
|
|
|
|
|
|
info "SSH Agent check passed (Ed25519 key found)"
|
|
|
|
|
|
return 0
|
|
|
|
|
|
fi
|
|
|
|
|
|
fi
|
|
|
|
|
|
fi
|
|
|
|
|
|
fi
|
|
|
|
|
|
|
|
|
|
|
|
# 現在の環境の SSH_AUTH_SOCK がセットかつ有効か(すでにシェルに Agent がある場合)
|
2026-01-18 07:52:01 +00:00
|
|
|
|
if [ -n "${SSH_AUTH_SOCK:-}" ] && [ -S "${SSH_AUTH_SOCK}" ]; then
|
2026-01-20 16:40:01 +09:00
|
|
|
|
# ssh-add で agent に接続できるか
|
2026-01-18 07:52:01 +00:00
|
|
|
|
if ssh-add -l >/dev/null 2>&1; then
|
2026-01-20 16:40:01 +09:00
|
|
|
|
# Ed25519 が載っているか確認
|
2026-01-18 07:52:01 +00:00
|
|
|
|
if ssh-add -l 2>/dev/null | grep -q "ED25519"; then
|
|
|
|
|
|
info "SSH Agent check passed (Ed25519 key found)"
|
|
|
|
|
|
return 0
|
|
|
|
|
|
fi
|
|
|
|
|
|
fi
|
|
|
|
|
|
fi
|
|
|
|
|
|
|
2026-01-20 16:40:01 +09:00
|
|
|
|
# ここに来た場合: (1) env ファイルがない (2) あっても ssh-add 失敗 (3) Ed25519 がない
|
|
|
|
|
|
# いずれも BWS から bootstrap を試す
|
2026-01-18 07:52:01 +00:00
|
|
|
|
info "No Ed25519 key found in SSH Agent. Attempting BWS bootstrap..."
|
|
|
|
|
|
|
2026-01-20 16:40:01 +09:00
|
|
|
|
# BWS トークン取得(環境変数、nbconf、または入力)
|
|
|
|
|
|
local bws_token
|
|
|
|
|
|
bws_token=$(get_bws_token) || true
|
2026-01-18 07:52:01 +00:00
|
|
|
|
if [ -z "$bws_token" ]; then
|
|
|
|
|
|
echo -n "Enter BWS_ACCESS_TOKEN: " >&2
|
2026-01-18 08:15:10 +00:00
|
|
|
|
read bws_token
|
2026-01-18 07:52:01 +00:00
|
|
|
|
if [ -z "$bws_token" ]; then
|
|
|
|
|
|
error "BWS_ACCESS_TOKEN is required when SSH Agent has no Ed25519 key"
|
|
|
|
|
|
fi
|
|
|
|
|
|
fi
|
|
|
|
|
|
|
2026-01-20 16:40:01 +09:00
|
|
|
|
# BWS からセットアップスクリプトを取得して実行
|
2026-01-18 07:52:01 +00:00
|
|
|
|
load_bws_setup "$bws_token"
|
|
|
|
|
|
|
2026-01-20 16:40:01 +09:00
|
|
|
|
# BWS セットアップ後に agent を再確認(env を読み直す)
|
|
|
|
|
|
if [ -f "$env_file" ]; then
|
|
|
|
|
|
source "$env_file"
|
|
|
|
|
|
fi
|
|
|
|
|
|
|
2026-01-18 07:52:01 +00:00
|
|
|
|
if [ -n "${SSH_AUTH_SOCK:-}" ] && [ -S "${SSH_AUTH_SOCK}" ]; then
|
2026-01-20 16:40:01 +09:00
|
|
|
|
if ssh-add -l >/dev/null 2>&1; then
|
|
|
|
|
|
if ssh-add -l 2>/dev/null | grep -q "ED25519"; then
|
|
|
|
|
|
info "SSH Agent check passed (Ed25519 key found after BWS setup)"
|
|
|
|
|
|
return 0
|
|
|
|
|
|
fi
|
|
|
|
|
|
else
|
|
|
|
|
|
# BWS セットアップ後に ssh-add が失敗
|
|
|
|
|
|
if [ -f "$env_file" ]; then
|
|
|
|
|
|
warn "SSH Agent communication failed after BWS setup."
|
|
|
|
|
|
warn "Please run: source $env_file"
|
|
|
|
|
|
error "SSH Agent communication failed. Run 'source $env_file' and try again."
|
|
|
|
|
|
else
|
|
|
|
|
|
error "SSH Agent communication failed after BWS setup."
|
|
|
|
|
|
fi
|
2026-01-18 07:52:01 +00:00
|
|
|
|
fi
|
|
|
|
|
|
fi
|
|
|
|
|
|
|
|
|
|
|
|
error "No Ed25519 key found in SSH Agent. Please add id_ed25519 with: ssh-add ~/.ssh/id_ed25519"
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
load_bws_setup() {
|
|
|
|
|
|
local token="$1"
|
2026-01-18 08:15:10 +00:00
|
|
|
|
local secret_id="6e70094b-6888-4fde-85f9-b3d6007fd68e"
|
2026-01-18 07:52:01 +00:00
|
|
|
|
|
2026-01-18 08:15:10 +00:00
|
|
|
|
info "Loading setup script from BWS (secret ID: $secret_id)..."
|
2026-01-18 07:52:01 +00:00
|
|
|
|
|
2026-01-20 16:40:01 +09:00
|
|
|
|
# bws が無ければインストールを試す
|
2026-01-18 07:52:01 +00:00
|
|
|
|
if ! command -v bws >/dev/null 2>&1; then
|
|
|
|
|
|
warn "bws command not found. Attempting to install..."
|
|
|
|
|
|
install_bws || error "Failed to install bws CLI"
|
|
|
|
|
|
fi
|
|
|
|
|
|
|
2026-01-20 16:40:01 +09:00
|
|
|
|
# bws 用にトークンを一時 export
|
2026-01-18 07:52:01 +00:00
|
|
|
|
export BWS_ACCESS_TOKEN="$token"
|
|
|
|
|
|
|
2026-01-20 16:40:01 +09:00
|
|
|
|
# 秘密 ID で BWS から secret を取得
|
2026-01-18 07:52:01 +00:00
|
|
|
|
local loader_script
|
|
|
|
|
|
if command -v jq >/dev/null 2>&1; then
|
2026-01-18 08:15:10 +00:00
|
|
|
|
loader_script=$(bws secret get "$secret_id" 2>&1 | jq -r '.value // empty')
|
2026-01-18 07:52:01 +00:00
|
|
|
|
elif command -v python3 >/dev/null 2>&1; then
|
2026-01-18 08:15:10 +00:00
|
|
|
|
loader_script=$(bws secret get "$secret_id" 2>&1 | python3 -c "import sys, json; print(json.load(sys.stdin).get('value', ''))" 2>/dev/null)
|
2026-01-18 07:52:01 +00:00
|
|
|
|
else
|
2026-01-20 16:40:01 +09:00
|
|
|
|
# フォールバック: grep/sed で value を取り出し(単純 JSON 用)
|
2026-01-18 08:15:10 +00:00
|
|
|
|
loader_script=$(bws secret get "$secret_id" 2>&1 | grep -o '"value": "[^"]*"' | sed 's/"value": "//;s/"$//' | head -1)
|
2026-01-18 07:52:01 +00:00
|
|
|
|
fi
|
|
|
|
|
|
|
|
|
|
|
|
if [ -z "$loader_script" ]; then
|
2026-01-18 08:15:10 +00:00
|
|
|
|
error "Failed to retrieve secret from BWS. Check your token and secret ID."
|
2026-01-18 07:52:01 +00:00
|
|
|
|
fi
|
|
|
|
|
|
|
2026-01-20 16:40:01 +09:00
|
|
|
|
# ローダースクリプトを実行
|
2026-01-18 07:52:01 +00:00
|
|
|
|
info "Executing BWS setup script..."
|
|
|
|
|
|
eval "$loader_script"
|
|
|
|
|
|
|
2026-01-20 16:40:01 +09:00
|
|
|
|
# 作成されていれば agent 用 env を読む
|
2026-01-18 07:52:01 +00:00
|
|
|
|
local env_file="/tmp/.nb_agent_env_${USER:-$(id -un)}"
|
|
|
|
|
|
if [ -f "$env_file" ]; then
|
|
|
|
|
|
source "$env_file"
|
|
|
|
|
|
info "SSH Agent environment loaded from $env_file"
|
|
|
|
|
|
fi
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-01-20 16:40:01 +09:00
|
|
|
|
keychain_cmd() {
|
|
|
|
|
|
local keep_mode="$1"
|
|
|
|
|
|
info "Fetching SSH keys from BWS and running ssh-add..."
|
|
|
|
|
|
|
|
|
|
|
|
local bws_token
|
|
|
|
|
|
bws_token=$(get_bws_token) || true
|
|
|
|
|
|
if [ -z "$bws_token" ]; then
|
|
|
|
|
|
echo -n "Enter BWS_ACCESS_TOKEN: " >&2
|
|
|
|
|
|
read bws_token
|
|
|
|
|
|
if [ -z "$bws_token" ]; then
|
|
|
|
|
|
error "BWS_ACCESS_TOKEN is required"
|
|
|
|
|
|
fi
|
|
|
|
|
|
fi
|
|
|
|
|
|
|
|
|
|
|
|
load_bws_setup "$bws_token"
|
|
|
|
|
|
|
|
|
|
|
|
if [ "$keep_mode" != "true" ]; then
|
|
|
|
|
|
info "Removing key files from disk (use -k/--keep to keep)..."
|
|
|
|
|
|
rm -f ~/.ssh/id_rsa ~/.ssh/id_ecdsa ~/.ssh/id_ed25519
|
|
|
|
|
|
fi
|
|
|
|
|
|
|
|
|
|
|
|
info "SSH agent setup complete."
|
|
|
|
|
|
ssh-add -l
|
|
|
|
|
|
print_source_agent_hint
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-01-22 01:57:07 +00:00
|
|
|
|
check_install_bws() {
|
|
|
|
|
|
# アーキテクチャチェック
|
|
|
|
|
|
local arch
|
|
|
|
|
|
arch=$(uname -m)
|
|
|
|
|
|
case "$arch" in
|
|
|
|
|
|
x86_64|aarch64|arm64)
|
|
|
|
|
|
;;
|
|
|
|
|
|
*)
|
|
|
|
|
|
return 1 # サポートされていないアーキテクチャ
|
|
|
|
|
|
;;
|
|
|
|
|
|
esac
|
|
|
|
|
|
|
|
|
|
|
|
# wget または curl が利用可能か
|
|
|
|
|
|
if ! command -v wget >/dev/null 2>&1 && ! command -v curl >/dev/null 2>&1; then
|
|
|
|
|
|
return 1
|
|
|
|
|
|
fi
|
|
|
|
|
|
|
|
|
|
|
|
# unzip が利用可能か、またはインストール可能か
|
|
|
|
|
|
if ! command -v unzip >/dev/null 2>&1; then
|
|
|
|
|
|
if ! command -v apt-get >/dev/null 2>&1 && ! command -v yum >/dev/null 2>&1; then
|
|
|
|
|
|
return 1
|
|
|
|
|
|
fi
|
|
|
|
|
|
fi
|
|
|
|
|
|
|
|
|
|
|
|
return 0
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-01-18 07:52:01 +00:00
|
|
|
|
install_bws() {
|
|
|
|
|
|
local arch
|
|
|
|
|
|
arch=$(uname -m)
|
|
|
|
|
|
local bws_version="1.0.0"
|
|
|
|
|
|
local bws_bin_dir="${HOME}/.local/bin"
|
|
|
|
|
|
local bws_path="${bws_bin_dir}/bws"
|
|
|
|
|
|
|
|
|
|
|
|
mkdir -p "$bws_bin_dir"
|
|
|
|
|
|
export PATH="$bws_bin_dir:$PATH"
|
|
|
|
|
|
|
2026-01-20 16:40:01 +09:00
|
|
|
|
# アーキテクチャ判定
|
2026-01-18 07:52:01 +00:00
|
|
|
|
case "$arch" in
|
|
|
|
|
|
x86_64)
|
|
|
|
|
|
arch="x86_64-unknown-linux-gnu"
|
|
|
|
|
|
;;
|
|
|
|
|
|
aarch64|arm64)
|
|
|
|
|
|
arch="aarch64-unknown-linux-gnu"
|
|
|
|
|
|
;;
|
|
|
|
|
|
*)
|
|
|
|
|
|
error "Unsupported architecture: $arch"
|
|
|
|
|
|
;;
|
|
|
|
|
|
esac
|
|
|
|
|
|
|
|
|
|
|
|
local zip_name="bws-${arch}-${bws_version}.zip"
|
|
|
|
|
|
local url="https://github.com/bitwarden/sdk-sm/releases/download/bws-v${bws_version}/${zip_name}"
|
|
|
|
|
|
|
|
|
|
|
|
info "Downloading bws v${bws_version} for ${arch}..."
|
|
|
|
|
|
|
2026-01-20 16:40:01 +09:00
|
|
|
|
# 必要なら unzip 等を入れる
|
2026-01-18 07:52:01 +00:00
|
|
|
|
if ! command -v unzip >/dev/null 2>&1; then
|
|
|
|
|
|
if command -v apt-get >/dev/null 2>&1; then
|
|
|
|
|
|
sudo apt-get update -qq && sudo apt-get install -y unzip >/dev/null 2>&1
|
|
|
|
|
|
elif command -v yum >/dev/null 2>&1; then
|
|
|
|
|
|
sudo yum install -y unzip >/dev/null 2>&1
|
2026-01-12 10:34:04 +00:00
|
|
|
|
fi
|
|
|
|
|
|
fi
|
|
|
|
|
|
|
2026-01-20 16:40:01 +09:00
|
|
|
|
# ダウンロードして展開
|
2026-01-18 07:52:01 +00:00
|
|
|
|
local temp_zip="/tmp/${zip_name}"
|
|
|
|
|
|
if command -v wget >/dev/null 2>&1; then
|
|
|
|
|
|
wget -q "$url" -O "$temp_zip" || return 1
|
|
|
|
|
|
elif command -v curl >/dev/null 2>&1; then
|
|
|
|
|
|
curl -sL "$url" -o "$temp_zip" || return 1
|
|
|
|
|
|
else
|
|
|
|
|
|
error "wget or curl is required to download bws"
|
2026-01-12 10:34:04 +00:00
|
|
|
|
fi
|
|
|
|
|
|
|
2026-01-18 07:52:01 +00:00
|
|
|
|
unzip -o "$temp_zip" -d "$bws_bin_dir" >/dev/null 2>&1 || return 1
|
|
|
|
|
|
chmod +x "$bws_path"
|
|
|
|
|
|
rm -f "$temp_zip"
|
|
|
|
|
|
|
|
|
|
|
|
info "bws installed successfully at $bws_path"
|
2026-01-12 10:34:04 +00:00
|
|
|
|
return 0
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
derive_key() {
|
2026-01-20 16:40:01 +09:00
|
|
|
|
# Ed25519 署名から暗号鍵を導出(同じメッセージなら同じ署名で決定的)
|
2026-01-22 01:57:07 +00:00
|
|
|
|
# 優先順位: 1. SSH Agent, 2. 秘密鍵ファイル, 3. BWS (check_ssh_agent で処理)
|
|
|
|
|
|
local check_mode=false
|
|
|
|
|
|
if [ "${1:-}" = "--check" ]; then
|
|
|
|
|
|
check_mode=true
|
|
|
|
|
|
fi
|
2026-01-12 10:34:04 +00:00
|
|
|
|
|
|
|
|
|
|
local temp_message=$(mktemp)
|
|
|
|
|
|
local temp_sig=$(mktemp)
|
|
|
|
|
|
local temp_pubkey=$(mktemp)
|
2026-01-20 16:40:01 +09:00
|
|
|
|
local temp_allowed=$(mktemp)
|
2026-01-12 10:34:04 +00:00
|
|
|
|
|
2026-01-20 16:40:01 +09:00
|
|
|
|
trap "rm -f '$temp_message' '$temp_sig' '$temp_pubkey' '$temp_allowed'" EXIT
|
2026-01-12 10:34:04 +00:00
|
|
|
|
|
2026-01-20 16:40:01 +09:00
|
|
|
|
# 署名するメッセージを作成
|
2026-01-12 10:34:04 +00:00
|
|
|
|
echo -n "$KEY_SEED_MESSAGE" > "$temp_message"
|
|
|
|
|
|
|
2026-01-20 16:40:01 +09:00
|
|
|
|
# 1. SSH Agent から Ed25519 の公開鍵を取得を試みる
|
2026-01-21 01:17:52 +00:00
|
|
|
|
ssh-add -L 2>/dev/null | grep "ssh-ed25519" | head -1 | awk '{print $1 " " $2}' > "$temp_pubkey" 2>/dev/null
|
|
|
|
|
|
if [ -s "$temp_pubkey" ]; then
|
2026-01-20 16:40:01 +09:00
|
|
|
|
# Agent から取得できた場合
|
2026-01-22 01:57:07 +00:00
|
|
|
|
# key1 オプションなしで試す(ssh-keygen -Y sign は allowed_keys 形式のファイルを期待するが、key1 オプションがあると読み込めない場合がある)
|
|
|
|
|
|
cat "$temp_pubkey" > "$temp_allowed"
|
2026-01-12 10:34:04 +00:00
|
|
|
|
|
2026-01-20 16:40:01 +09:00
|
|
|
|
# メッセージに署名(Agent経由)
|
|
|
|
|
|
if ssh-keygen -Y sign -f "$temp_allowed" -n "nbase2" < "$temp_message" > "$temp_sig" 2>/dev/null; then
|
2026-01-22 01:57:07 +00:00
|
|
|
|
if [ "$check_mode" = "true" ]; then
|
|
|
|
|
|
info "Signature obtained from: SSH Agent"
|
|
|
|
|
|
fi
|
2026-01-20 16:40:01 +09:00
|
|
|
|
# 署名を抜き出してハッシュし 256bit 鍵に
|
|
|
|
|
|
cat "$temp_sig" | base64 -d 2>/dev/null | sha256sum | head -c 64
|
|
|
|
|
|
return 0
|
2026-01-22 01:57:07 +00:00
|
|
|
|
else
|
|
|
|
|
|
if [ "$check_mode" = "true" ]; then
|
|
|
|
|
|
warn "Failed to sign with SSH Agent"
|
|
|
|
|
|
fi
|
|
|
|
|
|
fi
|
|
|
|
|
|
else
|
|
|
|
|
|
if [ "$check_mode" = "true" ]; then
|
|
|
|
|
|
warn "Failed to get Ed25519 key from SSH Agent"
|
2026-01-20 16:40:01 +09:00
|
|
|
|
fi
|
2026-01-12 10:34:04 +00:00
|
|
|
|
fi
|
|
|
|
|
|
|
2026-01-21 01:17:52 +00:00
|
|
|
|
# 2. 秘密鍵ファイルから直接署名を試みる
|
2026-01-20 16:40:01 +09:00
|
|
|
|
local key_file="${NB_KEY_FILE:-${HOME}/.ssh/id_ed25519}"
|
|
|
|
|
|
if [ -f "$key_file" ]; then
|
|
|
|
|
|
# 公開鍵を抽出
|
|
|
|
|
|
if ssh-keygen -y -f "$key_file" 2>/dev/null | awk '{print $1 " " $2}' > "$temp_pubkey" 2>/dev/null && [ -s "$temp_pubkey" ]; then
|
2026-01-22 01:57:07 +00:00
|
|
|
|
# 秘密鍵ファイルを使って署名(-f で秘密鍵ファイルを直接指定)
|
2026-01-20 16:40:01 +09:00
|
|
|
|
if ssh-keygen -Y sign -f "$key_file" -n "nbase2" < "$temp_message" > "$temp_sig" 2>/dev/null; then
|
2026-01-22 01:57:07 +00:00
|
|
|
|
if [ "$check_mode" = "true" ]; then
|
|
|
|
|
|
info "Signature obtained from: id_ed25519"
|
|
|
|
|
|
fi
|
2026-01-20 16:40:01 +09:00
|
|
|
|
# 署名を抜き出してハッシュし 256bit 鍵に
|
|
|
|
|
|
cat "$temp_sig" | base64 -d 2>/dev/null | sha256sum | head -c 64
|
|
|
|
|
|
return 0
|
2026-01-22 01:57:07 +00:00
|
|
|
|
else
|
|
|
|
|
|
if [ "$check_mode" = "true" ]; then
|
|
|
|
|
|
warn "Failed to sign with $key_file"
|
|
|
|
|
|
fi
|
|
|
|
|
|
fi
|
|
|
|
|
|
else
|
|
|
|
|
|
if [ "$check_mode" = "true" ]; then
|
|
|
|
|
|
warn "Failed to extract public key from $key_file"
|
2026-01-20 16:40:01 +09:00
|
|
|
|
fi
|
|
|
|
|
|
fi
|
2026-01-22 01:57:07 +00:00
|
|
|
|
else
|
|
|
|
|
|
if [ "$check_mode" = "true" ]; then
|
|
|
|
|
|
warn "Private key file not found: $key_file"
|
2026-01-21 01:17:52 +00:00
|
|
|
|
fi
|
|
|
|
|
|
fi
|
|
|
|
|
|
|
2026-01-22 01:57:07 +00:00
|
|
|
|
# 3. どれも失敗した場合、エラー(BWS は check_ssh_agent で処理される)
|
|
|
|
|
|
if [ "$check_mode" = "true" ]; then
|
|
|
|
|
|
warn "Could not derive key from SSH Agent or private key file. Ensure SSH Agent has Ed25519, or set NB_KEY_FILE."
|
|
|
|
|
|
return 1
|
|
|
|
|
|
else
|
|
|
|
|
|
error "Could not derive key from SSH Agent or private key file. Ensure SSH Agent has Ed25519, or set NB_KEY_FILE."
|
|
|
|
|
|
fi
|
2026-01-12 10:34:04 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
encrypt_file() {
|
|
|
|
|
|
local input="$1"
|
|
|
|
|
|
local output="$2"
|
|
|
|
|
|
|
|
|
|
|
|
if [ ! -f "$input" ]; then
|
|
|
|
|
|
error "Input file not found: $input"
|
|
|
|
|
|
fi
|
|
|
|
|
|
|
2026-01-22 01:57:07 +00:00
|
|
|
|
# 秘密鍵ファイルがある場合は check_ssh_agent をスキップ
|
2026-01-20 16:40:01 +09:00
|
|
|
|
local key_file="${NB_KEY_FILE:-${HOME}/.ssh/id_ed25519}"
|
2026-01-22 01:57:07 +00:00
|
|
|
|
if [ ! -f "$key_file" ]; then
|
2026-01-20 16:40:01 +09:00
|
|
|
|
check_ssh_agent
|
|
|
|
|
|
fi
|
2026-01-12 10:34:04 +00:00
|
|
|
|
|
2026-01-20 16:40:01 +09:00
|
|
|
|
info "Deriving encryption key..."
|
2026-01-12 10:34:04 +00:00
|
|
|
|
local key=$(derive_key)
|
|
|
|
|
|
|
|
|
|
|
|
if [ -z "$key" ]; then
|
|
|
|
|
|
error "Failed to derive encryption key"
|
|
|
|
|
|
fi
|
|
|
|
|
|
|
|
|
|
|
|
info "Encrypting $input -> $output..."
|
|
|
|
|
|
|
2026-01-20 16:40:01 +09:00
|
|
|
|
# OpenSSL で AES-256-CBC 暗号化(鍵は hex で渡す)
|
2026-01-12 10:34:04 +00:00
|
|
|
|
openssl enc -aes-256-cbc -salt -pbkdf2 -in "$input" -out "$output" -pass "pass:$key"
|
|
|
|
|
|
|
|
|
|
|
|
if [ $? -eq 0 ]; then
|
|
|
|
|
|
info "✅ Encryption successful: $output"
|
|
|
|
|
|
else
|
|
|
|
|
|
error "Encryption failed"
|
|
|
|
|
|
fi
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
decrypt_file() {
|
2026-01-18 08:15:10 +00:00
|
|
|
|
local force_mode="$1"
|
|
|
|
|
|
local input="$2"
|
|
|
|
|
|
local output="$3"
|
2026-01-12 10:34:04 +00:00
|
|
|
|
|
|
|
|
|
|
if [ ! -f "$input" ]; then
|
|
|
|
|
|
error "Input file not found: $input"
|
|
|
|
|
|
fi
|
|
|
|
|
|
|
2026-01-20 16:40:01 +09:00
|
|
|
|
# force: SSH Agent チェックを飛ばし BWS から直接読む
|
2026-01-18 08:15:10 +00:00
|
|
|
|
if [ "$force_mode" = "true" ]; then
|
|
|
|
|
|
info "Force mode: Loading setup script from BWS..."
|
2026-01-20 16:40:01 +09:00
|
|
|
|
local bws_token
|
|
|
|
|
|
bws_token=$(get_bws_token) || true
|
2026-01-18 08:15:10 +00:00
|
|
|
|
if [ -z "$bws_token" ]; then
|
|
|
|
|
|
echo -n "Enter BWS_ACCESS_TOKEN: " >&2
|
|
|
|
|
|
read bws_token
|
|
|
|
|
|
if [ -z "$bws_token" ]; then
|
|
|
|
|
|
error "BWS_ACCESS_TOKEN is required in force mode"
|
|
|
|
|
|
fi
|
|
|
|
|
|
fi
|
|
|
|
|
|
load_bws_setup "$bws_token"
|
2026-01-20 16:40:01 +09:00
|
|
|
|
# BWS セットアップ後に agent を再確認
|
2026-01-18 08:15:10 +00:00
|
|
|
|
if [ -z "${SSH_AUTH_SOCK:-}" ] || [ ! -S "${SSH_AUTH_SOCK}" ]; then
|
|
|
|
|
|
error "SSH Agent not available after BWS setup"
|
|
|
|
|
|
fi
|
|
|
|
|
|
if ! ssh-add -l >/dev/null 2>&1 || ! ssh-add -l 2>/dev/null | grep -q "ED25519"; then
|
|
|
|
|
|
error "Ed25519 key not found in SSH Agent after BWS setup"
|
|
|
|
|
|
fi
|
|
|
|
|
|
else
|
2026-01-22 01:57:07 +00:00
|
|
|
|
# 秘密鍵ファイルがある場合は check_ssh_agent をスキップ
|
2026-01-20 16:40:01 +09:00
|
|
|
|
local key_file="${NB_KEY_FILE:-${HOME}/.ssh/id_ed25519}"
|
2026-01-22 01:57:07 +00:00
|
|
|
|
if [ ! -f "$key_file" ]; then
|
2026-01-20 16:40:01 +09:00
|
|
|
|
check_ssh_agent
|
|
|
|
|
|
fi
|
2026-01-18 08:15:10 +00:00
|
|
|
|
fi
|
2026-01-12 10:34:04 +00:00
|
|
|
|
|
2026-01-20 16:40:01 +09:00
|
|
|
|
info "Deriving decryption key..."
|
2026-01-12 10:34:04 +00:00
|
|
|
|
local key=$(derive_key)
|
|
|
|
|
|
|
|
|
|
|
|
if [ -z "$key" ]; then
|
|
|
|
|
|
error "Failed to derive decryption key"
|
|
|
|
|
|
fi
|
|
|
|
|
|
|
|
|
|
|
|
info "Decrypting $input -> $output..."
|
|
|
|
|
|
|
2026-01-20 16:40:01 +09:00
|
|
|
|
# OpenSSL で AES-256-CBC 復号
|
2026-01-12 10:34:04 +00:00
|
|
|
|
if openssl enc -aes-256-cbc -d -salt -pbkdf2 -in "$input" -out "$output" -pass "pass:$key" 2>/dev/null; then
|
|
|
|
|
|
info "✅ Decryption successful: $output"
|
|
|
|
|
|
else
|
|
|
|
|
|
error "Decryption failed (wrong key or corrupted file)"
|
|
|
|
|
|
fi
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-01-20 16:40:01 +09:00
|
|
|
|
encrypt_text() {
|
|
|
|
|
|
local input_text="$1"
|
|
|
|
|
|
|
2026-01-22 01:57:07 +00:00
|
|
|
|
# 秘密鍵ファイルがある場合は check_ssh_agent をスキップ
|
2026-01-20 16:40:01 +09:00
|
|
|
|
local key_file="${NB_KEY_FILE:-${HOME}/.ssh/id_ed25519}"
|
2026-01-22 01:57:07 +00:00
|
|
|
|
if [ ! -f "$key_file" ]; then
|
2026-01-20 16:40:01 +09:00
|
|
|
|
check_ssh_agent
|
|
|
|
|
|
fi
|
|
|
|
|
|
|
|
|
|
|
|
info "Deriving encryption key..."
|
|
|
|
|
|
local key=$(derive_key)
|
|
|
|
|
|
|
|
|
|
|
|
if [ -z "$key" ]; then
|
|
|
|
|
|
error "Failed to derive encryption key"
|
|
|
|
|
|
fi
|
|
|
|
|
|
|
|
|
|
|
|
# 標準入力から読み取る場合
|
|
|
|
|
|
if [ "$input_text" = "-" ]; then
|
|
|
|
|
|
local temp_input=$(mktemp)
|
|
|
|
|
|
cat > "$temp_input"
|
|
|
|
|
|
input_text=$(cat "$temp_input")
|
|
|
|
|
|
rm -f "$temp_input"
|
|
|
|
|
|
fi
|
|
|
|
|
|
|
|
|
|
|
|
# OpenSSL で暗号化して Base64 エンコード
|
|
|
|
|
|
echo -n "$input_text" | openssl enc -aes-256-cbc -salt -pbkdf2 -pass "pass:$key" | base64 -w 0
|
|
|
|
|
|
echo "" # 改行を追加
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
decrypt_text() {
|
|
|
|
|
|
local input_text="$1"
|
|
|
|
|
|
|
2026-01-22 01:57:07 +00:00
|
|
|
|
# 秘密鍵ファイルがある場合は check_ssh_agent をスキップ
|
2026-01-20 16:40:01 +09:00
|
|
|
|
local key_file="${NB_KEY_FILE:-${HOME}/.ssh/id_ed25519}"
|
2026-01-22 01:57:07 +00:00
|
|
|
|
if [ ! -f "$key_file" ]; then
|
2026-01-20 16:40:01 +09:00
|
|
|
|
check_ssh_agent
|
|
|
|
|
|
fi
|
|
|
|
|
|
|
|
|
|
|
|
info "Deriving decryption key..."
|
|
|
|
|
|
local key=$(derive_key)
|
|
|
|
|
|
|
|
|
|
|
|
if [ -z "$key" ]; then
|
|
|
|
|
|
error "Failed to derive decryption key"
|
|
|
|
|
|
fi
|
|
|
|
|
|
|
|
|
|
|
|
# 標準入力から読み取る場合
|
|
|
|
|
|
if [ "$input_text" = "-" ]; then
|
|
|
|
|
|
input_text=$(cat)
|
|
|
|
|
|
fi
|
|
|
|
|
|
|
|
|
|
|
|
# Base64 デコードして OpenSSL で復号
|
|
|
|
|
|
echo -n "$input_text" | base64 -d 2>/dev/null | openssl enc -aes-256-cbc -d -salt -pbkdf2 -pass "pass:$key" 2>/dev/null || error "Decryption failed (wrong key or corrupted data)"
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
# メインのコマンド分岐
|
2026-01-12 10:34:04 +00:00
|
|
|
|
main() {
|
|
|
|
|
|
if [ $# -eq 0 ]; then
|
|
|
|
|
|
usage
|
|
|
|
|
|
fi
|
|
|
|
|
|
|
|
|
|
|
|
local command="$1"
|
|
|
|
|
|
shift
|
|
|
|
|
|
|
2026-01-20 16:40:01 +09:00
|
|
|
|
# install-bws, keychain の場合は check_dependencies をスキップ
|
|
|
|
|
|
if [ "$command" != "install-bws" ] && [ "$command" != "keychain" ] && [ "$command" != "help" ] && [ "$command" != "--help" ] && [ "$command" != "-h" ]; then
|
2026-01-18 07:52:01 +00:00
|
|
|
|
check_dependencies
|
|
|
|
|
|
fi
|
|
|
|
|
|
|
2026-01-12 10:34:04 +00:00
|
|
|
|
case "$command" in
|
2026-01-20 16:40:01 +09:00
|
|
|
|
enc|enctext)
|
|
|
|
|
|
if [ $# -lt 1 ]; then
|
|
|
|
|
|
error "enc requires 1 argument: <text> or '-' for stdin"
|
|
|
|
|
|
fi
|
|
|
|
|
|
encrypt_text "${1:-}"
|
|
|
|
|
|
;;
|
|
|
|
|
|
dec|dectext)
|
|
|
|
|
|
if [ $# -lt 1 ]; then
|
|
|
|
|
|
error "dec requires 1 argument: <text> or '-' for stdin"
|
|
|
|
|
|
fi
|
|
|
|
|
|
decrypt_text "${1:-}"
|
|
|
|
|
|
;;
|
|
|
|
|
|
encfile|encrypt)
|
2026-01-12 10:34:04 +00:00
|
|
|
|
if [ $# -ne 2 ]; then
|
2026-01-20 16:40:01 +09:00
|
|
|
|
error "encfile requires 2 arguments: <input> <output>"
|
2026-01-12 10:34:04 +00:00
|
|
|
|
fi
|
|
|
|
|
|
encrypt_file "$1" "$2"
|
|
|
|
|
|
;;
|
2026-01-20 16:40:01 +09:00
|
|
|
|
decfile|decrypt)
|
2026-01-18 08:15:10 +00:00
|
|
|
|
local force_mode=false
|
|
|
|
|
|
local input=""
|
|
|
|
|
|
local output=""
|
|
|
|
|
|
|
2026-01-20 16:40:01 +09:00
|
|
|
|
# 引数解析
|
2026-01-18 08:15:10 +00:00
|
|
|
|
while [ $# -gt 0 ]; do
|
|
|
|
|
|
case "$1" in
|
|
|
|
|
|
-f|--force)
|
|
|
|
|
|
force_mode=true
|
|
|
|
|
|
shift
|
|
|
|
|
|
;;
|
|
|
|
|
|
-*)
|
|
|
|
|
|
error "Unknown option: $1"
|
|
|
|
|
|
;;
|
|
|
|
|
|
*)
|
|
|
|
|
|
if [ -z "$input" ]; then
|
|
|
|
|
|
input="$1"
|
|
|
|
|
|
elif [ -z "$output" ]; then
|
|
|
|
|
|
output="$1"
|
|
|
|
|
|
else
|
|
|
|
|
|
error "Too many arguments for decrypt"
|
|
|
|
|
|
fi
|
|
|
|
|
|
shift
|
|
|
|
|
|
;;
|
|
|
|
|
|
esac
|
|
|
|
|
|
done
|
|
|
|
|
|
|
|
|
|
|
|
if [ -z "$input" ] || [ -z "$output" ]; then
|
2026-01-20 16:40:01 +09:00
|
|
|
|
error "decfile requires 2 arguments: <input> <output>"
|
2026-01-12 10:34:04 +00:00
|
|
|
|
fi
|
2026-01-18 08:15:10 +00:00
|
|
|
|
|
|
|
|
|
|
decrypt_file "$force_mode" "$input" "$output"
|
2026-01-12 10:34:04 +00:00
|
|
|
|
;;
|
|
|
|
|
|
check)
|
2026-01-22 01:57:07 +00:00
|
|
|
|
info "Testing signature derivation..."
|
|
|
|
|
|
if derive_key --check >/dev/null; then
|
|
|
|
|
|
echo "✅ Signature derivation successful"
|
|
|
|
|
|
else
|
|
|
|
|
|
warn "All signature methods failed"
|
|
|
|
|
|
info "Checking BWS availability..."
|
|
|
|
|
|
|
|
|
|
|
|
# bws コマンドが利用可能か
|
|
|
|
|
|
if command -v bws >/dev/null 2>&1; then
|
|
|
|
|
|
info "bws command is available"
|
|
|
|
|
|
else
|
|
|
|
|
|
if check_install_bws; then
|
|
|
|
|
|
info "bws can be installed (run 'nbcrypt install-bws')"
|
|
|
|
|
|
else
|
|
|
|
|
|
warn "bws installation is not possible"
|
|
|
|
|
|
fi
|
|
|
|
|
|
fi
|
|
|
|
|
|
|
|
|
|
|
|
# BWS_ACCESS_TOKEN の取得を試みる
|
|
|
|
|
|
local bws_token
|
|
|
|
|
|
if bws_token=$(get_bws_token 2>/dev/null); then
|
|
|
|
|
|
info "BWS_ACCESS_TOKEN is available"
|
|
|
|
|
|
if command -v bws >/dev/null 2>&1; then
|
|
|
|
|
|
info "BWS setup is available. Run 'nbcrypt keychain' to set up SSH Agent."
|
|
|
|
|
|
else
|
|
|
|
|
|
info "BWS_ACCESS_TOKEN is available but bws command is not installed."
|
|
|
|
|
|
fi
|
|
|
|
|
|
else
|
|
|
|
|
|
warn "BWS_ACCESS_TOKEN is not available (checked: nbconf, environment variable)"
|
|
|
|
|
|
fi
|
|
|
|
|
|
fi
|
2026-01-12 10:34:04 +00:00
|
|
|
|
;;
|
2026-01-20 16:40:01 +09:00
|
|
|
|
keychain)
|
|
|
|
|
|
local keep_mode=false
|
|
|
|
|
|
while [ $# -gt 0 ]; do
|
|
|
|
|
|
case "$1" in
|
|
|
|
|
|
-k|--keep)
|
|
|
|
|
|
keep_mode=true
|
|
|
|
|
|
shift
|
|
|
|
|
|
;;
|
|
|
|
|
|
-*)
|
|
|
|
|
|
error "Unknown option: $1"
|
|
|
|
|
|
;;
|
|
|
|
|
|
*)
|
|
|
|
|
|
error "keychain accepts only -k/--keep"
|
|
|
|
|
|
;;
|
|
|
|
|
|
esac
|
|
|
|
|
|
done
|
|
|
|
|
|
keychain_cmd "$keep_mode"
|
|
|
|
|
|
;;
|
2026-01-18 07:52:01 +00:00
|
|
|
|
install-bws)
|
|
|
|
|
|
install_bws
|
|
|
|
|
|
;;
|
2026-01-12 10:34:04 +00:00
|
|
|
|
help|--help|-h)
|
|
|
|
|
|
usage
|
|
|
|
|
|
;;
|
|
|
|
|
|
*)
|
|
|
|
|
|
error "Unknown command: $command (use 'help' for usage)"
|
|
|
|
|
|
;;
|
|
|
|
|
|
esac
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
main "$@"
|
|
|
|
|
|
|