From a974ab4f514e5ef42a0b60dedef769e676e948b7 Mon Sep 17 00:00:00 2001 From: Matthew Raymer Date: Tue, 4 Mar 2025 12:32:19 +0000 Subject: [PATCH] refactor: improve DID generation and error handling - Convert did_generator.sh to output clean JSON - Add structured error reporting with stages - Improve debug logging with DEBUG flag - Better error handling in run-deeplink-tests.sh - Add detailed debug tracing - Fix JSON parsing and validation - Add visual feedback for generated DIDs - Use printf instead of echo for consistent output - Remove stderr mixing with stdout - Add proper exit status handling This refactors the DID generation process to be more reliable and maintainable by using structured JSON output and proper error handling throughout the pipeline. --- test-scripts/check-did.sh | 2 +- test-scripts/did_generator.sh | 207 ++++++++++++++++++++--------- test-scripts/run-deeplink-tests.sh | 82 ++++++++++-- 3 files changed, 211 insertions(+), 80 deletions(-) diff --git a/test-scripts/check-did.sh b/test-scripts/check-did.sh index 8a5861b..f54b82f 100755 --- a/test-scripts/check-did.sh +++ b/test-scripts/check-did.sh @@ -3,7 +3,7 @@ # Usage: ./check-did.sh [did] # If no DID provided, lists all visible DIDs -API_URL=${ENDORSER_API_URL:-"https://test-api.endorser.ch/api/v2/claim"} +API_URL=${ENDORSER_API_URL:-"https://test-api.endorser.ch/api/whichDidsICanSee"} ADMIN_DID="did:ethr:0x0000694B58C2cC69658993A90D3840C560f2F51F" ADMIN_KEY="2b6472c026ec2aa2c4235c994a63868fc9212d18b58f6cbfe861b52e71330f5b" diff --git a/test-scripts/did_generator.sh b/test-scripts/did_generator.sh index c22a1b0..3e6cc19 100755 --- a/test-scripts/did_generator.sh +++ b/test-scripts/did_generator.sh @@ -18,10 +18,18 @@ fi 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/v2/claim"} +DEBUG=${DEBUG:-0} + +# Function to log debug info +debug_log() { + if [ "$DEBUG" = "1" ]; then + echo "$@" >&2 + fi +} # Function to generate a new keypair generate_keypair() { - echo "Generating new keypair..." + debug_log "Generating new keypair..." # Create a temporary directory for key operations TMPDIR=$(mktemp -d) @@ -52,12 +60,13 @@ generate_keypair() { # Create DID DID="did:ethr:0x$ADDRESS" - echo "Generated DID Details:" - echo "----------------------" - echo "DID: $DID" - echo "Address: 0x$ADDRESS" - echo "Private Key: $PRIVATE_KEY" - echo "Public Key: $PUBLIC_KEY" + # Print debug info if enabled + debug_log "Generated DID Details:" + debug_log "----------------------" + debug_log "DID: $DID" + debug_log "Address: $ADDRESS" + debug_log "Private Key: $PRIVATE_KEY" + debug_log "Public Key: $PUBLIC_KEY" # Export variables for other functions export DID PRIVATE_KEY PUBLIC_KEY ADDRESS @@ -69,6 +78,67 @@ hex_to_dec() { printf "%d" "0x$1" } +# Function to attempt signing with retries +attempt_signing() { + local max_attempts=5 + local attempt=1 + + while [ $attempt -le $max_attempts ]; do + debug_log "Signing attempt $attempt of $max_attempts..." + + # Sign message and get DER format signature + openssl dgst -sha256 -sign "$TMPDIR/private.pem" "$TMPDIR/message.txt" 2>/dev/null > "$TMPDIR/signature.der" + + # Convert DER signature to hex + der_sig=$(xxd -p -c 256 "$TMPDIR/signature.der") + + debug_log "Debug - Full DER signature:" + debug_log "$der_sig" + + # Parse DER structure with length checking + 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 + seq_len="${BASH_REMATCH[1]}" + 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)) + + debug_log "R length: $r_len_dec bytes" + debug_log "S length: $s_len_dec bytes" + + # Handle R value + 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 + 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 + + # Validate final lengths + if [ ${#r_val} -eq 64 ] && [ ${#s_val} -eq 64 ]; then + debug_log "Valid signature found on attempt $attempt" + return 0 + fi + fi + + debug_log "Invalid signature on attempt $attempt, retrying..." + attempt=$((attempt + 1)) + done + + echo "Failed to generate valid signature after $max_attempts attempts" >&2 + return 1 +} + # Function to create and sign JWT create_jwt() { local now=$(date +%s) @@ -103,58 +173,25 @@ create_jwt() { # Write message to file echo -n "$message" > "$TMPDIR/message.txt" - # Sign message and get DER format signature - openssl dgst -sha256 -sign "$TMPDIR/private.pem" "$TMPDIR/message.txt" 2>/dev/null > "$TMPDIR/signature.der" - - # Convert DER signature to R+S format - der_sig=$(xxd -p -c 256 "$TMPDIR/signature.der") - - # Debug the full DER signature - echo "Debug - Full DER signature:" - echo "$der_sig" - - # Parse DER structure - more robust version - 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 - seq_len="${BASH_REMATCH[1]}" - r_len="${BASH_REMATCH[2]}" - r_val="${BASH_REMATCH[3]}" - s_len="${BASH_REMATCH[4]}" - s_val="${BASH_REMATCH[5]}" - - echo "Debug - DER parsing:" - echo "Sequence length: $seq_len" - echo "R length: $r_len" - echo "R value: $r_val" - echo "S length: $s_len" - echo "S value: $s_val" - - # Remove leading zeros if present - r_val=${r_val#"00"} - s_val=${s_val#"00"} - - # Pad R and S to 32 bytes each - r_val=$(printf "%064s" "$r_val" | tr ' ' '0') - s_val=$(printf "%064s" "$s_val" | tr ' ' '0') + # Attempt signing with retries + if attempt_signing; then + # Create final JWT + concat_sig="${r_val}${s_val}" + signature=$(echo -n "$concat_sig" | xxd -r -p | base64 -w 0 | tr '/+' '_-' | tr -d '=') + JWT="$message.$signature" - # Concatenate R+S and convert to base64url - signature=$(echo -n "${r_val}${s_val}" | xxd -r -p | base64 -w 0 | tr '/+' '_-' | tr -d '=') + debug_log "Created JWT: ${JWT:0:50}..." + export JWT + return 0 else - echo "Error: Invalid DER signature format" - echo "DER: $der_sig" + echo "Failed to generate valid signature" >&2 return 1 fi - - # Create final JWT - JWT="$message.$signature" - - echo -e "\nCreated JWT: ${JWT:0:50}..." - export JWT - return 0 } # Function to register DID register_did() { - echo "Attempting registration..." + debug_log "Attempting registration..." # Create request body body="{\"jwtEncoded\":\"$JWT\"}" @@ -166,24 +203,64 @@ register_did() { # Check response if echo "$response" | jq -e '.success' >/dev/null 2>&1; then - echo "Registration successful!" - echo "Response:" - echo "$response" | jq '.' + debug_log "Registration successful!" + debug_log "Response: $(echo "$response" | jq -c '.')" + + # Output only the JSON result + debug_log "About to output JSON..." + jq -n \ + --arg did "$DID" \ + --arg private_key "$PRIVATE_KEY" \ + --arg registration_id "$(echo "$response" | jq -r '.success.registrationId')" \ + --arg claim_id "$(echo "$response" | jq -r '.success.claimId')" \ + '{ + status: "success", + did: $did, + privateKey: $private_key, + registrationId: $registration_id, + claimId: $claim_id + }' + debug_log "JSON output complete" + exit 0 else - echo "Registration failed!" - echo "Error: $(echo "$response" | jq -r '.error.message // empty')" - echo "Full response:" - echo "$response" | jq '.' || echo "$response" + error_msg=$(echo "$response" | jq -r '.error.message // "Unknown error"') + debug_log "Registration failed: $error_msg" + debug_log "Full response: $(echo "$response" | jq -c '.' || echo "$response")" + + # Output error as JSON + jq -n \ + --arg error "$error_msg" \ + --arg stage "registration" \ + '{ + status: "error", + stage: $stage, + error: $error + }' + exit 1 fi } # Main execution -echo "Starting DID Generation..." -echo "Using admin DID: $ADMIN_DID" -echo "API URL: $API_URL" +debug_log "Starting DID Generation..." +debug_log "Using admin DID: $ADMIN_DID" +debug_log "API URL: $API_URL" -generate_keypair && \ -create_jwt && \ -register_did +if ! generate_keypair; then + jq -n '{ + status: "error", + stage: "keypair", + error: "Failed to generate keypair" + }' + exit 1 +fi + +if ! create_jwt; then + jq -n '{ + status: "error", + stage: "jwt", + error: "Failed to generate valid signature" + }' + exit 1 +fi -exit $? \ No newline at end of file +register_did \ No newline at end of file diff --git a/test-scripts/run-deeplink-tests.sh b/test-scripts/run-deeplink-tests.sh index 8f87b50..91de29f 100755 --- a/test-scripts/run-deeplink-tests.sh +++ b/test-scripts/run-deeplink-tests.sh @@ -41,27 +41,81 @@ else # Function to extract DID info from did_generator.sh output extract_did_info() { local output="$1" - local did=$(echo "$output" | grep "^DID: " | cut -d' ' -f2-) - local private_key=$(echo "$output" | grep "^Private Key: " | cut -d' ' -f3-) - printf '%s\n' "{\"did\":\"$did\",\"privateKey\":\"$private_key\"}" + + # Debug: Show what we received + echo "DEBUG: extract_did_info received: '$output'" >&2 + + # Parse and validate JSON + if ! printf '%s' "$output" | jq -e 'if type == "object" and .status == "success" then true else false end' >/dev/null 2>&1; then + echo "Error: DID generation failed" >&2 + echo "Error details:" >&2 + if ! printf '%s' "$output" | jq -e . >/dev/null 2>&1; then + echo "Invalid JSON output: $output" >&2 + else + printf '%s' "$output" | jq -r '"\(.stage): \(.error // "Unknown error")"' >&2 + fi + return 1 + fi + + # Return the successful JSON + printf '%s' "$output" } echo "Generating first contact DID..." - CONTACT1_INFO=$(./test-scripts/did_generator.sh 2>&1 | tee /dev/stderr | extract_did_info) - CONTACT1_DID=$(echo "$CONTACT1_INFO" | jq -r .did) - CONTACT1_KEY=$(echo "$CONTACT1_INFO" | jq -r .privateKey) - echo "Extracted DID: $CONTACT1_DID" - echo "Extracted Key: $CONTACT1_KEY" + # Debug: Show raw command output + DEBUG_OUTPUT=$(DEBUG=0 ./test-scripts/did_generator.sh) + GEN_STATUS=$? + echo "DEBUG: Raw did_generator.sh output: '$DEBUG_OUTPUT'" >&2 + + if [ $GEN_STATUS -ne 0 ]; then + echo "Error: did_generator.sh failed with status $GEN_STATUS" >&2 + exit 1 + fi + + CONTACT1_OUTPUT=$(printf '%s' "$DEBUG_OUTPUT" | tr -d '\r') + echo "DEBUG: After tr command: '$CONTACT1_OUTPUT'" >&2 + + # Debug: Show what we're passing to extract_did_info + echo "DEBUG: Calling extract_did_info with: '$CONTACT1_OUTPUT'" >&2 + CONTACT1_INFO=$(extract_did_info "$CONTACT1_OUTPUT") + EXTRACT_STATUS=$? + echo "DEBUG: extract_did_info returned status: $EXTRACT_STATUS" >&2 + echo "DEBUG: CONTACT1_INFO: '$CONTACT1_INFO'" >&2 + + if [ $EXTRACT_STATUS -ne 0 ]; then + echo "DEBUG: extract_did_info failed" >&2 + exit 1 + fi + CONTACT1_DID=$(printf '%s' "$CONTACT1_INFO" | jq -r .did) + CONTACT1_KEY=$(printf '%s' "$CONTACT1_INFO" | jq -r .privateKey) echo "Generating second contact DID (Jordan)..." - CONTACT2_INFO=$(./test-scripts/did_generator.sh 2>&1 | tee /dev/stderr | extract_did_info) - CONTACT2_DID=$(echo "$CONTACT2_INFO" | jq -r .did) - CONTACT2_KEY=$(echo "$CONTACT2_INFO" | jq -r .privateKey) + DEBUG_OUTPUT=$(DEBUG=0 ./test-scripts/did_generator.sh) + CONTACT2_OUTPUT=$(printf '%s' "$DEBUG_OUTPUT" | tr -d '\r') + CONTACT2_INFO=$(extract_did_info "$CONTACT2_OUTPUT") + if [ $? -ne 0 ]; then + exit 1 + fi + CONTACT2_DID=$(printf '%s' "$CONTACT2_INFO" | jq -r .did) + CONTACT2_KEY=$(printf '%s' "$CONTACT2_INFO" | jq -r .privateKey) echo "Generating issuer DID..." - ISSUER_INFO=$(./test-scripts/did_generator.sh 2>&1 | tee /dev/stderr | extract_did_info) - ISSUER_DID=$(echo "$ISSUER_INFO" | jq -r .did) - ISSUER_KEY=$(echo "$ISSUER_INFO" | jq -r .privateKey) + DEBUG_OUTPUT=$(DEBUG=0 ./test-scripts/did_generator.sh) + ISSUER_OUTPUT=$(printf '%s' "$DEBUG_OUTPUT" | tr -d '\r') + ISSUER_INFO=$(extract_did_info "$ISSUER_OUTPUT") + if [ $? -ne 0 ]; then + exit 1 + fi + ISSUER_DID=$(printf '%s' "$ISSUER_INFO" | jq -r .did) + ISSUER_KEY=$(printf '%s' "$ISSUER_INFO" | jq -r .privateKey) + + # Add some visual feedback about the generated DIDs + echo + echo "Generated DIDs:" + echo " Contact 1: ${CONTACT1_DID:0:20}..." + echo " Contact 2: ${CONTACT2_DID:0:20}..." + echo " Issuer: ${ISSUER_DID:0:20}..." + echo # Create a temporary env file with the generated DIDs echo "Creating test-env.sh with generated DIDs..."