diff --git a/test-scripts/dids_seen.sh b/test-scripts/dids_seen.sh new file mode 100755 index 0000000..138e0e8 --- /dev/null +++ b/test-scripts/dids_seen.sh @@ -0,0 +1,186 @@ +#!/bin/bash +# DID Visibility Check Script +# @author Matthew Raymer +# +# This script checks visibility permissions for DIDs within the endorser.ch system. +# It creates a signed JWT using admin credentials and queries the visibility API. +# +# Features: +# - JWT creation and signing using ES256K-R +# - DID visibility checking +# - Environment variable support +# - Debug logging +# - Command line argument parsing +# +# Usage: +# ./dids_seen.sh [-d did_to_check] +# DEBUG=1 ./dids_seen.sh # For debug output +# +# Environment Variables: +# ADMIN_DID - Admin DID for authorization +# ADMIN_PRIVATE_KEY - Private key for signing +# ENDORSER_API_URL - API endpoint (defaults to test) +# DEBUG - Enable debug logging when set to 1 + +# Add debug logging function +debug_log() { + if [ "${DEBUG:-0}" = "1" ]; then + echo "DEBUG: $*" >&2 + fi +} + +# Parse command line arguments +# -d: Specific DID to check visibility for +CHECK_DID="" +while getopts "d:" opt; do + case $opt in + d) CHECK_DID="$OPTARG" ;; + \?) echo "Usage: $0 [-d did_to_check]" >&2; exit 1 ;; + esac +done + +# Load environment variables from .env file if present +# Supports: +# - ADMIN_DID +# - ADMIN_PRIVATE_KEY +# - ENDORSER_API_URL +if [ -f .env ]; then + export $(cat .env | grep -v '^#' | xargs) +fi + +# Default values for required parameters +ADMIN_DID=${ADMIN_DID:-"did:ethr:0x0000694B58C2cC69658993A90D3840C560f2F51F"} +ADMIN_PRIVATE_KEY=${ADMIN_PRIVATE_KEY:-"2b6472c026ec2aa2c4235c994a63868fc9212d18b58f6cbfe861b52e71330f5b"} +API_URL=${ENDORSER_API_URL:-"https://test-api.endorser.ch/api/report/whichDidsICanSee"} + +# Create JWT payload with: +# - Issuer (iss) +# - Subject (sub) +# - Issued At (iat) +# - Expiration (exp) +now=$(date +%s) +exp=$((now + 86400)) # 24 hours from now + +payload=$(jq -n \ + --arg iss "$ADMIN_DID" \ + --arg sub "$ADMIN_DID" \ + --arg iat "$now" \ + --arg exp "$exp" \ + '{ + iss: $iss, + sub: $sub, + iat: ($iat | tonumber), + exp: ($exp | tonumber) + }') + +# Base64url encode header and payload +# Header specifies ES256K-R algorithm for Ethereum compatibility +header='{"alg":"ES256K-R","typ":"JWT"}' +header_b64=$(echo -n "$header" | base64 -w 0 | tr '/+' '_-' | tr -d '=') +payload_b64=$(echo -n "$payload" | base64 -w 0 | tr '/+' '_-' | tr -d '=') + +# Create message to sign (header.payload) +message="$header_b64.$payload_b64" + +# Create temporary directory for key operations +# Uses trap to ensure cleanup on exit +TMPDIR=$(mktemp -d) +trap 'rm -rf "$TMPDIR"' EXIT + +# Create private key PEM file +# Converts raw private key to PEM format for OpenSSL +echo "-----BEGIN EC PRIVATE KEY-----" > "$TMPDIR/private.pem" +( + echo "302e0201010420" # Private key header + echo -n "$ADMIN_PRIVATE_KEY" # Private key bytes + echo "a00706052b8104000a" # secp256k1 OID +) | xxd -r -p | base64 >> "$TMPDIR/private.pem" +echo "-----END EC PRIVATE KEY-----" >> "$TMPDIR/private.pem" + +# Write message to file for signing +echo -n "$message" > "$TMPDIR/message.txt" + +# Sign message and get DER format signature +openssl dgst -sha256 -sign "$TMPDIR/private.pem" "$TMPDIR/message.txt" > "$TMPDIR/signature.der" + +# Convert DER signature to hex +der_sig=$(xxd -p -c 256 "$TMPDIR/signature.der") + +# Parse DER structure +# Extracts R and S values from signature +if [[ $der_sig =~ ^30([0-9a-f]{2})02([0-9a-f]{2})([0-9a-f]*)02([0-9a-f]{2})([0-9a-f]*)$ ]]; then + r_len="${BASH_REMATCH[2]}" + r_val="${BASH_REMATCH[3]}" + s_len="${BASH_REMATCH[4]}" + s_val="${BASH_REMATCH[5]}" + + # Convert lengths to decimal + r_len_dec=$((16#$r_len)) + s_len_dec=$((16#$s_len)) + + # Handle R value padding + if [ $r_len_dec -gt 32 ]; then + r_val=${r_val: -64} # Take last 32 bytes + elif [ $r_len_dec -lt 32 ]; then + r_val=$(printf "%064s" "$r_val" | tr ' ' '0') # Left pad + fi + + # Handle S value padding + if [ $s_len_dec -gt 32 ]; then + s_val=${s_val: -64} # Take last 32 bytes + elif [ $s_len_dec -lt 32 ]; then + s_val=$(printf "%064s" "$s_val" | tr ' ' '0') # Left pad + fi + + # Create final signature + concat_sig="${r_val}${s_val}" + + # Calculate recovery bit (v) + # Try each possible recovery value (0-3) until we find one that works + for v in {0..3}; do + recovery_sig=$(printf "%s%02x" "$concat_sig" "$v") + debug_log "Trying recovery bit $v: $recovery_sig" + + # Convert to base64url + signature=$(echo -n "$recovery_sig" | xxd -r -p | base64 -w 0 | tr '/+' '_-' | tr -d '=') + + # Create JWT with this signature + JWT="$message.$signature" + + # Test the JWT against the API + response=$(curl -s -X GET "$API_URL" \ + -H "Content-Type: application/json" \ + -H "Authorization: Bearer $JWT") + + # Check if the JWT worked + if ! echo "$response" | grep -q "JWT_VERIFY_FAILED"; then + echo "JWT Token:" + echo "$JWT" + echo + echo "Export command:" + echo "export TOKEN='$JWT'" + echo + if [ -n "$CHECK_DID" ]; then + # Check if specific DID is in the list + if echo "$response" | jq -e --arg did "$CHECK_DID" 'contains([$did])' > /dev/null; then + echo "✅ DID $CHECK_DID is in the list" + exit 0 + else + echo "❌ DID $CHECK_DID is not in the list" + exit 1 + fi + else + # Show full list of visible DIDs + echo "$response" | jq '.' --indent 2 + fi + exit 0 + fi + done + + echo "Error: Could not find valid recovery bit" + exit 1 +else + echo "Error: Invalid DER signature format" + echo "DER: $der_sig" + exit 1 +fi