5 changed files with 497 additions and 80 deletions
			
			
		| @ -1,3 +1,4 @@ | |||||
|  | eth_keys | ||||
| pywebview | pywebview | ||||
| pyinstaller>=6.12.0 | pyinstaller>=6.12.0 | ||||
| # For development | # 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