5 changed files with 497 additions and 80 deletions
			
			
		| @ -1,3 +1,4 @@ | |||
| eth_keys | |||
| pywebview | |||
| pyinstaller>=6.12.0 | |||
| # For development | |||
|  | |||
| @ -0,0 +1,284 @@ | |||
| #!/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 | |||
| 
 | |||
| # Enhanced debug logging | |||
| 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" | |||
| 
 | |||
| # Add debug points | |||
| debug_log "Creating JWT with:" | |||
| debug_log "ADMIN_DID: $ADMIN_DID" | |||
| debug_log "API_URL: $API_URL" | |||
| debug_log "Payload: $payload" | |||
| debug_log "Header base64: $header_b64" | |||
| debug_log "Payload base64: $payload_b64" | |||
| debug_log "Message to sign: $message" | |||
| 
 | |||
| # 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]}" | |||
|      | |||
|     debug_log "Raw signature values:" | |||
|     debug_log "  R (${#r_val} chars): $r_val" | |||
|     debug_log "  S (${#s_val} chars): $s_val" | |||
|      | |||
|     # 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 | |||
|      | |||
|     # Ensure both values are exactly 64 characters | |||
|     r_val=$(printf "%064s" "$r_val" | tr ' ' '0') | |||
|     s_val=$(printf "%064s" "$s_val" | tr ' ' '0') | |||
|      | |||
|     debug_log "Normalized values:" | |||
|     debug_log "  R (${#r_val} chars): $r_val" | |||
|     debug_log "  S (${#s_val} chars): $s_val" | |||
|      | |||
|     # Create final signature | |||
|     concat_sig="${r_val}${s_val}" | |||
|      | |||
|     # Debug the DER parsing | |||
|     debug_log "DER Signature Analysis:" | |||
|     debug_log "  Full DER: $der_sig" | |||
|     debug_log "  Sequence length: ${BASH_REMATCH[1]}" | |||
|     debug_log "  R length: $r_len ($r_len_dec bytes)" | |||
|     debug_log "  S length: $s_len ($s_len_dec bytes)" | |||
|      | |||
|     # Debug signature components | |||
|     debug_log "Signature components:" | |||
|     debug_log "  R value: $r_val (length: ${#r_val})" | |||
|     debug_log "  S value: $s_val (length: ${#s_val})" | |||
|     debug_log "  Concatenated: $concat_sig (length: ${#concat_sig})" | |||
|      | |||
|     # Try both normal and high-S value signatures | |||
|     s_val_alt="" | |||
|     if [ $s_len_dec -gt 32 ]; then | |||
|         # Store alternative S value | |||
|         s_val_alt="$s_val" | |||
|         # Calculate N - s for high-S values | |||
|         n="FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141" | |||
|         # Use Python for hex math to avoid bc issues | |||
|         normalized_s=$(python3 -c " | |||
|             n = int('$n', 16) | |||
|             s = int('${s_val}', 16) | |||
|             result = hex(n - s)[2:].zfill(64) | |||
|             print(result.lower()) | |||
|         " 2>/dev/null || echo "ERROR") | |||
|         if [ "$normalized_s" = "ERROR" ]; then | |||
|             debug_log "  Failed to normalize S value" | |||
|             # Keep original s_val if normalization fails | |||
|             s_val_alt="" | |||
|         else | |||
|             s_val=$(printf "%064s" "$normalized_s" | tr ' ' '0' | tr '[:upper:]' '[:lower:]') | |||
|             debug_log "  Normalized S value: $s_val" | |||
|         fi | |||
|     fi | |||
|      | |||
|     # Calculate recovery bit (v) | |||
|     # Try each possible recovery value (0-3) until we find one that works | |||
|     for v in {0..3}; do | |||
|         # Try both S values if we have an alternative | |||
|         s_values=("$s_val") | |||
|         if [ -n "$s_val_alt" ]; then | |||
|             s_values+=("$s_val_alt") | |||
|         fi | |||
|          | |||
|         for current_s in "${s_values[@]}"; do | |||
|             concat_sig="${r_val}${current_s}" | |||
|             debug_log "Trying with S value: $current_s" | |||
|              | |||
|             recovery_sig=$(printf "%s%02x" "$concat_sig" "$v") | |||
|             debug_log "Recovery attempt $v:" | |||
|             debug_log "  Concatenated signature: $concat_sig" | |||
|             debug_log "  Recovery signature: $recovery_sig" | |||
|             debug_log "  Recovery signature length: ${#recovery_sig}" | |||
|              | |||
|             # Ensure signature is exactly 65 bytes (130 hex chars) | |||
|             if [ ${#recovery_sig} -ne 130 ]; then | |||
|                 debug_log "  Invalid signature length: ${#recovery_sig}, expected 130" | |||
|                 continue | |||
|             fi | |||
|              | |||
|             # Convert hex to binary, then to base64url | |||
|             signature=$(echo -n "$recovery_sig" | xxd -r -p 2>/dev/null | base64 -w 0 | tr '/+' '_-' | tr -d '=') | |||
|             debug_log "  Base64URL signature: $signature" | |||
|              | |||
|             # Create JWT with this signature | |||
|             JWT="$message.$signature" | |||
|             debug_log "  Testing JWT: $JWT" | |||
|              | |||
|             # Test the JWT against the API | |||
|             response=$(curl -s -X GET "$API_URL" \ | |||
|                  -H "Content-Type: application/json" \ | |||
|                  -H "Authorization: Bearer $JWT") | |||
|             debug_log "  API Response: $response" | |||
|              | |||
|             # Check if the JWT worked | |||
|             if ! echo "$response" | grep -q "JWT.*failed"; then | |||
|                 echo "JWT Token:" | |||
|                 echo "$JWT" | |||
|                 debug_log "Success with v=$v and S=${current_s:0:8}..." | |||
|                 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" | |||
|                         echo "Attempting to add visibility..." | |||
|                          | |||
|                         # Request visibility | |||
|                         visibility_response=$(curl -s -X POST \ | |||
|                             'http://test-api.endorser.ch/api/report/canSeeMe' \ | |||
|                             -H "Authorization: Bearer $JWT" \ | |||
|                             -H "Content-Type: application/json" \ | |||
|                             -d "{\"did\": \"$CHECK_DID\"}") | |||
|                          | |||
|                         if echo "$visibility_response" | grep -q "error"; then | |||
|                             echo "❌ Failed to add visibility:" | |||
|                             echo "$visibility_response" | jq '.' --indent 2 | |||
|                             exit 1 | |||
|                         else | |||
|                             echo "✅ Successfully requested visibility. Please try checking again." | |||
|                             exit 0 | |||
|                         fi | |||
|                     fi | |||
|                 else | |||
|                     # Show full list of visible DIDs | |||
|                     echo "$response" | jq '.' --indent 2 | |||
|                 fi | |||
|                 exit 0 | |||
|             fi | |||
|         done | |||
|     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 | |||
					Loading…
					
					
				
		Reference in new issue