You can not select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
		
			
		
			
				
					
					
						
							188 lines
						
					
					
						
							5.4 KiB
						
					
					
				
			
		
		
		
			
			
			
				
					
				
				
					
				
			
		
		
	
	
							188 lines
						
					
					
						
							5.4 KiB
						
					
					
				| #!/bin/bash | |
|  | |
| # DID Creation and Registration Flow | |
| # @author Matthew Raymer | |
| # | |
| # This script implements the creation and registration of Decentralized Identifiers (DIDs) | |
| # with the endorser.ch service. It matches the Python and TypeScript implementations. | |
| # | |
| # Flow: | |
| # 1. Generate or load mnemonic seed phrase | |
| # 2. Derive Ethereum keys and address | |
| # 3. Create DID identifier | |
| # 4. Initialize account | |
| # 5. Create signed JWT | |
| # 6. Register DID with endorser service | |
| 
 | |
| # Constants | |
| API_SERVER=${ENDORSER_API_URL:-"https://test-api.endorser.ch"} | |
| ENDORSER_DID="did:ethr:0x0000694B58C2cC69658993A90D3840C560f2F51F" | |
| ENDORSER_PRIVATE_KEY="2b6472c026ec2aa2c4235c994a63868fc9212d18b58f6cbfe861b52e71330f5b" | |
| 
 | |
| # Create temporary directory for operations | |
| TMPDIR=$(mktemp -d) | |
| trap 'rm -rf "$TMPDIR"' EXIT | |
| 
 | |
| initialize_account() { | |
|     # Generate or load mnemonic | |
|     if [ ! -f "mnemonic.txt" ]; then | |
|         # Generate entropy and convert to hex | |
|         openssl rand -hex 32 > mnemonic.txt | |
|     fi | |
|      | |
|     # Read entropy | |
|     ENTROPY=$(cat mnemonic.txt) | |
|      | |
|     # Create temporary directory for key operations | |
|     TMPDIR=$(mktemp -d) | |
|     trap 'rm -rf "$TMPDIR"' EXIT | |
|      | |
|     # Generate secp256k1 private key | |
|     openssl ecparam -name secp256k1 -genkey -noout -out "$TMPDIR/private.pem" | |
|      | |
|     # Extract private key in hex format | |
|     PRIVATE_KEY=$(openssl ec -in "$TMPDIR/private.pem" -text -noout 2>/dev/null |  | |
|         grep priv -A 3 | tail -n +2 | tr -d '\n[:space:]:' | cut -c3-) | |
|      | |
|     # Generate public key and address | |
|     PUBLIC_KEY=$(openssl ec -in "$TMPDIR/private.pem" -pubout -outform DER 2>/dev/null |  | |
|         tail -c 65 | xxd -p -c 65) | |
|      | |
|     # Generate Ethereum address (last 20 bytes of keccak256 of public key) | |
|     ADDRESS=$(echo -n "$PUBLIC_KEY" | xxd -r -p |  | |
|         openssl dgst -sha3-256 -binary |  | |
|         tail -c 20 | xxd -p) | |
|      | |
|     # Create identity JSON | |
|     IDENTITY=$(cat <<EOF | |
| { | |
|     "did": "did:ethr:0x${ADDRESS}", | |
|     "keys": [{ | |
|         "id": "did:ethr:0x${ADDRESS}#keys-1", | |
|         "type": "Secp256k1VerificationKey2018", | |
|         "controller": "did:ethr:0x${ADDRESS}", | |
|         "ethereumAddress": "0x${ADDRESS}", | |
|         "publicKeyHex": "${PUBLIC_KEY}", | |
|         "privateKeyHex": "${PRIVATE_KEY}" | |
|     }], | |
|     "services": [] | |
| } | |
| EOF | |
| ) | |
| 
 | |
|     echo "Account initialized:" | |
|     echo "$IDENTITY" | jq . | |
|     echo | |
|      | |
|     # Export for other functions | |
|     export IDENTITY | |
| } | |
| 
 | |
| create_endorser_jwt() { | |
|     local did="$1" | |
|     local private_key="$2" | |
|     local payload="$3" | |
|     local sub_did="$4" | |
|      | |
|     # Create JWT header and payload | |
|     local now=$(date +%s) | |
|     local exp=$((now + 3600)) | |
|      | |
|     local header='{"typ":"JWT","alg":"ES256K"}' | |
|     local jwt_payload=$(echo "$payload" | jq --arg iss "$did" \ | |
|         --arg iat "$now" \ | |
|         --arg exp "$exp" \ | |
|         '. + {iss: $iss, iat: ($iat|tonumber), exp: ($exp|tonumber)}') | |
|      | |
|     # Base64URL encode header and payload | |
|     local header_b64=$(echo -n "$header" | base64 -w 0 | tr '/+' '_-' | tr -d '=') | |
|     local payload_b64=$(echo -n "$jwt_payload" | base64 -w 0 | tr '/+' '_-' | tr -d '=') | |
|     local message="$header_b64.$payload_b64" | |
|      | |
|     # Create temporary directory | |
|     local TMPDIR=$(mktemp -d) | |
|     trap 'rm -rf "$TMPDIR"' EXIT | |
|      | |
|     # Create private key in SEC1 format | |
|     ( | |
|         echo -n "$private_key"  # Private key bytes | |
|     ) | xxd -r -p > "$TMPDIR/private.key" | |
|      | |
|     # Hash the message | |
|     echo -n "$message" | openssl dgst -sha256 -binary -out "$TMPDIR/message.hash" | |
|      | |
|     # Sign using bitcoin-cli (or similar tool that handles secp256k1 correctly) | |
|     if command -v bitcoin-cli &> /dev/null; then | |
|         # Use bitcoin-cli if available | |
|         signature=$(bitcoin-cli signmessagewithprivkey \ | |
|             "$(cat "$TMPDIR/private.key" | xxd -p -c 64)" \ | |
|             "$(cat "$TMPDIR/message.hash" | xxd -p -c 32)") | |
|     else | |
|         # Fallback to custom secp256k1 signing | |
|         signature=$(secp256k1-sign "$TMPDIR/private.key" "$TMPDIR/message.hash") | |
|     fi | |
|      | |
|     echo "$message.$signature" | |
| } | |
| 
 | |
| register() { | |
|     local active_did="$1" | |
|      | |
|     echo "Endorser DID: $ENDORSER_DID" | |
|     echo "Active DID: $active_did" | |
|      | |
|     # Create registration claim | |
|     local vc_payload=$(cat <<EOF | |
| { | |
|     "vc": { | |
|         "@context": ["https://www.w3.org/2018/credentials/v1"], | |
|         "type": ["VerifiableCredential"], | |
|         "credentialSubject": { | |
|             "@context": "https://schema.org", | |
|             "@type": "RegisterAction", | |
|             "agent": { | |
|                 "identifier": "$ENDORSER_DID" | |
|             }, | |
|             "participant": { | |
|                 "identifier": "$active_did" | |
|             }, | |
|             "object": "endorser.ch", | |
|             "endTime": $(( $(date +%s) + 7*24*60*60 )) | |
|         } | |
|     } | |
| } | |
| EOF | |
| ) | |
| 
 | |
|     echo "Registration Claim:" | |
|     echo "$vc_payload" | jq . | |
|     echo | |
| 
 | |
|     # Create and sign JWT | |
|     local jwt_token=$(create_endorser_jwt "$ENDORSER_DID" "$ENDORSER_PRIVATE_KEY" "$vc_payload" "$active_did") | |
|      | |
|     echo "Generated JWT:" | |
|     echo "$jwt_token" | |
|     echo | |
|      | |
|     # Submit registration | |
|     local response=$(curl -s -X POST "$API_SERVER/api/v2/claim" \ | |
|         -H "Content-Type: application/json" \ | |
|         -d "{\"jwtEncoded\": \"$jwt_token\"}") | |
|      | |
|     echo "Registration response:" | |
|     echo "$response" | jq . | |
| } | |
| 
 | |
| main() { | |
|     initialize_account | |
|      | |
|     # Extract DID and private key from identity | |
|     local active_did=$(echo "$IDENTITY" | jq -r .did) | |
|     local private_key=$(echo "$IDENTITY" | jq -r .keys[0].privateKeyHex) | |
|      | |
|     # Register DID | |
|     register "$active_did" | |
| } | |
| 
 | |
| main "$@"  |