From cc10dab3a4523daef2f57aa219a779fd0c63f7f2 Mon Sep 17 00:00:00 2001 From: Matthew Raymer Date: Wed, 5 Mar 2025 14:08:27 +0000 Subject: [PATCH] feat: add shell implementation of DID registration flow - Match Python/TypeScript implementations - Use consistent JWT signing approach - Maintain payload compatibility - Add detailed documentation --- test-scripts/new_flow.sh | 176 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 176 insertions(+) create mode 100755 test-scripts/new_flow.sh diff --git a/test-scripts/new_flow.sh b/test-scripts/new_flow.sh new file mode 100755 index 0000000..dc610fd --- /dev/null +++ b/test-scripts/new_flow.sh @@ -0,0 +1,176 @@ +#!/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 <