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 "$@" 
							 |