#!/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 24-word mnemonic using Python python3 -c " from eth_account.hdaccount import generate_mnemonic print(generate_mnemonic(language='english')) " > mnemonic.txt fi # Read and process mnemonic MNEMONIC=$(cat mnemonic.txt) # Derive address and keys using Python IDENTITY=$(python3 -c " from eth_account import Account from eth_keys import keys import json Account.enable_unaudited_hdwallet_features() mnemonic = '$MNEMONIC'.strip() account = Account.from_mnemonic(mnemonic) address = account.address private_key = account.key.hex()[2:] pk = keys.PrivateKey(account.key) public_key = pk.public_key.to_hex()[2:] identity = { 'did': f'did:ethr:{address}', 'keys': [{ 'id': f'did:ethr:{address}#keys-1', 'type': 'Secp256k1VerificationKey2018', 'controller': f'did:ethr:{address}', 'ethereumAddress': address, 'publicKeyHex': public_key, 'privateKeyHex': private_key }], 'services': [] } print(json.dumps(identity)) ") 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" # Sign using Python eth_keys (matching TypeScript ES256K implementation) local signature=$(python3 -c " from eth_keys import keys import hashlib import base64 private_key_bytes = bytes.fromhex('$private_key') private_key = keys.PrivateKey(private_key_bytes) message_hash = hashlib.sha256('$message'.encode()).digest() signature = private_key.sign_msg_hash(message_hash) signature_bytes = signature.r.to_bytes(32, 'big') + signature.s.to_bytes(32, 'big') print(base64.urlsafe_b64encode(signature_bytes).decode().rstrip('=')) ") 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 <