forked from trent_larson/crowd-funder-for-time-pwa
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.
This commit is contained in:
@@ -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"
|
||||
|
||||
|
||||
@@ -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]}"
|
||||
# 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"
|
||||
|
||||
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')
|
||||
|
||||
# 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
|
||||
|
||||
exit $?
|
||||
if ! create_jwt; then
|
||||
jq -n '{
|
||||
status: "error",
|
||||
stage: "jwt",
|
||||
error: "Failed to generate valid signature"
|
||||
}'
|
||||
exit 1
|
||||
fi
|
||||
|
||||
register_did
|
||||
@@ -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..."
|
||||
|
||||
Reference in New Issue
Block a user