#!/bin/bash # Check for required commands for cmd in openssl xxd jq curl base64; do if ! command -v $cmd &> /dev/null; then echo "Error: $cmd is not installed. Please install it first." echo "On Arch Linux: sudo pacman -S $cmd" exit 1 fi done # Load environment variables if [ -f .env ]; then export $(cat .env | grep -v '^#' | xargs) fi # Default values 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() { debug_log "Generating new keypair..." # Create a temporary directory for key operations TMPDIR=$(mktemp -d) trap 'rm -rf "$TMPDIR"' EXIT # Generate private key openssl ecparam -name secp256k1 -genkey -noout > "$TMPDIR/private.pem" # Extract raw private key PRIVATE_KEY=$(openssl ec -in "$TMPDIR/private.pem" -text -noout 2>/dev/null | \ grep priv -A 3 | tail -n +2 | \ tr -d '\n[:space:]:' | \ sed 's/^00//') # Generate public key (compressed format) PUBLIC_KEY=$(openssl ec -in "$TMPDIR/private.pem" -pubout -outform DER 2>/dev/null | \ tail -c 65 | \ xxd -p -c 65 | \ sed 's/^04/02/') # Generate Ethereum address ADDRESS=$(echo -n "$PUBLIC_KEY" | \ xxd -r -p | \ openssl dgst -sha3-256 -binary | \ tail -c 20 | \ xxd -p) # Create DID DID="did:ethr:0x$ADDRESS" # 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 return 0 } # Function to hex_to_dec 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) local exp=$((now + 300)) # Create header (compact JSON) local header='{"typ":"JWT","alg":"ES256K"}' # Create registration claim (compact JSON) local claim="{\"iat\":$now,\"exp\":$exp,\"sub\":\"RegisterAction\",\"vc\":{\"@context\":[\"https://www.w3.org/2018/credentials/v1\"],\"type\":[\"VerifiableCredential\"],\"credentialSubject\":{\"@context\":\"https://schema.org\",\"@type\":\"RegisterAction\",\"agent\":{\"did\":\"$ADMIN_DID\"},\"participant\":{\"did\":\"$DID\"},\"object\":\"endorser.ch\"}},\"iss\":\"$ADMIN_DID\"}" # Base64url encode header and claim header_b64=$(echo -n "$header" | base64 -w 0 | tr '/+' '_-' | tr -d '=') claim_b64=$(echo -n "$claim" | base64 -w 0 | tr '/+' '_-' | tr -d '=') # Create message to sign message="$header_b64.$claim_b64" # Create temporary directory for key operations TMPDIR=$(mktemp -d) trap 'rm -rf "$TMPDIR"' EXIT # Create private key PEM file 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 echo -n "$message" > "$TMPDIR/message.txt" # 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" debug_log "Created JWT: ${JWT:0:50}..." export JWT return 0 else echo "Failed to generate valid signature" >&2 return 1 fi } # Function to register DID register_did() { debug_log "Attempting registration..." # Create request body body="{\"jwtEncoded\":\"$JWT\"}" # Send registration request response=$(curl -s -X POST "$API_URL" \ -H "Content-Type: application/json" \ -d "$body") # Check response if echo "$response" | jq -e '.success' >/dev/null 2>&1; then 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 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 debug_log "Starting DID Generation..." debug_log "Using admin DID: $ADMIN_DID" debug_log "API URL: $API_URL" 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 register_did