fix: consolidate deep link testing scripts

- Remove redundant test-deeplinks.sh script
- Update run-deeplink-tests.sh with improved timeout
- Remove unused secp256k1-sign script
- Remove deprecated new_flow scripts
- Clean up test script directory

Technical Changes:
- Increase default timeout from 5 to 10 seconds
- Remove Python/TypeScript DID generation scripts
- Remove redundant deep link test script
- Remove unused crypto signing script

This simplifies the test script directory by removing
deprecated scripts and consolidating deep link testing
into a single, more reliable script.
This commit is contained in:
Matthew Raymer
2025-03-11 10:39:41 +00:00
parent e2d543337b
commit 9e56fbf373
220 changed files with 9 additions and 11902 deletions

View File

@@ -22,12 +22,14 @@ The `run-deeplink-tests.sh` script tests the app's deep link handling capabiliti
1. Generate required test files using either:
Python method:
```bash
pip install mnemonic eth_account eth_keys web3 requests
python test-scripts/new_flow.py
python test-scripts/generate_data.py
```
OR TypeScript method:
```bash
npm install
npm run build
@@ -42,16 +44,19 @@ The `run-deeplink-tests.sh` script tests the app's deep link handling capabiliti
### Running Tests
1. Execute tests:
```bash
./test-scripts/run-deeplink-tests.sh
```
2. Print mode (no device needed):
```bash
./test-scripts/run-deeplink-tests.sh -p
```
3. Custom timeout:
```bash
./test-scripts/run-deeplink-tests.sh -t 10
```
@@ -59,6 +64,7 @@ The `run-deeplink-tests.sh` script tests the app's deep link handling capabiliti
### Troubleshooting
If you encounter errors:
1. Ensure `.generated` directory exists with required files
2. Check ADB is installed for device testing
3. Run with `-p` flag to verify deep link generation
@@ -67,7 +73,7 @@ If you encounter errors:
### Clean Up
To clean up generated files:
```bash
rm -rf .generated
```

View File

@@ -1,60 +0,0 @@
#!/bin/bash
# Requirements: jq, python, and `pip install -r requirements.txt`
# Usage: ./check-did.sh [did]
# If no DID provided, lists all visible DIDs
API_URL=${ENDORSER_API_URL:-"https://test-api.endorser.ch/api"}
ADMIN_DID="did:ethr:0x0000694B58C2cC69658993A90D3840C560f2F51F"
ADMIN_KEY="2b6472c026ec2aa2c4235c994a63868fc9212d18b58f6cbfe861b52e71330f5b"
# Create verification JWT using Python (equivalent to uport-credentials)
jwt=$(python3 -c "
from eth_keys import keys
import hashlib, base64, json, time
# Create header and payload
header = {'typ': 'JWT', 'alg': 'ES256K'}
payload = {
'iss': '$ADMIN_DID',
'exp': int(time.time()) + 300 # 5 minutes from now
}
# Base64url encode header and payload
def b64url(data):
return base64.urlsafe_b64encode(json.dumps(data).encode()).decode().rstrip('=')
header_b64 = b64url(header)
payload_b64 = b64url(payload)
message = f'{header_b64}.{payload_b64}'
# Sign using admin key
private_key = keys.PrivateKey(bytes.fromhex('$ADMIN_KEY'))
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')
signature_b64 = base64.urlsafe_b64encode(signature_bytes).decode().rstrip('=')
# Output complete JWT
print(f'{message}.{signature_b64}')
")
REQUEST_URL="$API_URL/report/whichDidsICanSee"
echo "Making request to: $REQUEST_URL"
echo "Getting visible DIDs..."
response=$(curl -s -X GET "$REQUEST_URL" \
-H "Authorization: Bearer $jwt" \
-H "Content-Type: application/json")
echo -e "\nResponse:"
echo "$response" | jq '.'
# If specific DID provided, check if it's in the list
if [ -n "$1" ]; then
echo -e "\nChecking if DID $1 is visible..."
if echo "$response" | jq -e --arg did "$1" '.[] | select(. == $did)' > /dev/null; then
echo "✅ DID is registered and visible"
else
echo "❌ DID not found in visible list"
fi
fi

View File

@@ -1,364 +0,0 @@
"""
DID Generator Script
@author Matthew Raymer
This script generates and registers Decentralized Identifiers (DIDs) with admin authorization.
It supports the creation of Ethereum-based DIDs (did:ethr) and handles the complete
registration flow with the endorser.ch API.
Features:
- Ethereum keypair generation with compressed public keys
- JWT creation and signing using ES256K
- DID registration with admin authorization
- Detailed error handling and logging
- Command-line interface for admin DID input
Dependencies:
eth_account: For Ethereum account operations
eth_keys: For key manipulation and compression
requests: For API communication
secrets: For secure random number generation
hashlib: For SHA-256 hashing
base64: For JWT encoding
argparse: For command-line argument parsing
pathlib: For path handling
dotenv: For environment variable loading
os: For environment variable access
Usage:
python did_generator.py [options]
Options:
--admin-did <did> Override default admin DID
--api-url <url> Override default API endpoint
Environment:
Default Admin DID: did:ethr:0x0000694B58C2cC69658993A90D3840C560f2F51F
Default API URL: https://test-api.endorser.ch/api/v2/claim
"""
from eth_account import Account
import json
import base64
from eth_account.messages import encode_defunct
from eth_keys import keys
import time
import requests
import argparse
import secrets
import hashlib
from pathlib import Path
from dotenv import load_dotenv
import os
# Load environment variables
load_dotenv()
class DIDRegistration:
"""
Handles the creation and registration of DIDs with admin authorization.
This class manages the complete lifecycle of DID creation and registration:
1. Generating secure Ethereum keypairs with compressed public keys
2. Creating DIDs from public keys in did:ethr format
3. Signing registration claims with admin credentials
4. Submitting registration to the endorser.ch API
The registration process uses two keypairs:
1. Admin keypair: Used to sign and authorize the registration
2. New DID keypair: The identity being registered
Attributes:
api_url (str): Endpoint for DID registration
admin_keypair (dict): Admin's credentials containing:
- did: Admin's DID in did:ethr format
- private_key: Admin's private key for signing
"""
def __init__(self, admin_keypair: dict, api_url: str = None):
"""
Initialize DID registration with admin credentials.
Args:
admin_keypair (dict): Admin's DID and private key for signing
api_url (str, optional): Override default API URL
"""
self.api_url = api_url or "https://test-api.endorser.ch/api/v2/claim"
self.admin_keypair = admin_keypair # Store full admin keypair
Account.enable_unaudited_hdwallet_features()
def create_keypair(self) -> dict:
"""
Generate a new Ethereum keypair and associated DID.
Creates a secure random keypair and formats it for use with the
endorser.ch API. Uses compressed public key format to match
ethers.js implementation.
Returns:
dict: Keypair information containing:
- private_key: Raw private key without 0x prefix
- public_key: Compressed public key with 0x prefix
- address: Ethereum address
- did: Generated DID in did:ethr format
Security:
- Uses secrets module for cryptographically secure randomness
- Implements compressed public key format
- Maintains private key security
"""
private_key = secrets.token_hex(32)
# Create private key object and derive public key
private_key_bytes = bytes.fromhex(private_key)
private_key_obj = keys.PrivateKey(private_key_bytes)
# Get compressed public key (like ethers.js)
public_key_obj = private_key_obj.public_key
public_key = '0x' + public_key_obj.to_compressed_bytes().hex()
# Create account from private key (for address)
account = Account.from_key(private_key_bytes)
return {
'private_key': private_key, # No 0x prefix
'public_key': public_key, # With 0x prefix, compressed format
'address': account.address,
'did': f"did:ethr:{account.address}"
}
def sign_jwt(self, payload: dict, private_key: str, did: str) -> str:
"""
Sign a JWT using ES256K algorithm.
Creates and signs a JWT following the did-jwt specification:
1. Constructs header and payload
2. Base64url encodes components
3. Signs using ES256K
4. Assembles final JWT
Args:
payload (dict): JWT payload to sign
private_key (str): Private key for signing (without 0x prefix)
did (str): DID to use as issuer
Returns:
str: Signed JWT string in format: header.payload.signature
Security:
- Implements ES256K signing
- Follows did-jwt specification
- Handles message hashing correctly
"""
# Add issuer to payload like did-jwt does
full_payload = {
**payload,
"iss": did
}
header = {
"typ": "JWT",
"alg": "ES256K"
}
# Create the JWT segments
header_b64 = base64.urlsafe_b64encode(
json.dumps(header, separators=(',', ':')).encode()
).decode().rstrip('=')
payload_b64 = base64.urlsafe_b64encode(
json.dumps(full_payload, separators=(',', ':')).encode()
).decode().rstrip('=')
message = f"{header_b64}.{payload_b64}"
# Hash the message with sha256
message_hash = hashlib.sha256(message.encode()).digest()
# Sign using eth_keys directly
private_key_bytes = bytes.fromhex(private_key)
private_key_obj = keys.PrivateKey(private_key_bytes)
signature = private_key_obj.sign_msg_hash(message_hash)
# Get r and s from signature
r = signature.r.to_bytes(32, 'big')
s = signature.s.to_bytes(32, 'big')
signature_bytes = r + s
# Format signature
signature_b64 = base64.urlsafe_b64encode(signature_bytes).decode().rstrip('=')
return f"{message}.{signature_b64}"
def create_jwt(self, new_did: str) -> str:
"""
Create a signed JWT for DID registration
Args:
new_did (str): The DID being registered
"""
now = int(time.time())
# Create registration claim with admin as agent
register_claim = {
"@context": "https://schema.org",
"@type": "RegisterAction",
"agent": { "did": self.admin_keypair['did'] },
"participant": { "did": new_did },
"object": "endorser.ch"
}
payload = {
"iat": now,
"exp": now + 300,
"sub": "RegisterAction",
"vc": {
"@context": ["https://www.w3.org/2018/credentials/v1"],
"type": ["VerifiableCredential"],
"credentialSubject": register_claim
}
}
print(f"\nDebug - JWT payload: {json.dumps(payload, indent=2)}")
# Sign with admin's private key
return self.sign_jwt(
payload,
self.admin_keypair['private_key'],
self.admin_keypair['did']
)
def register_did(self, jwt_token: str) -> dict:
"""
Submit DID registration to the endorser.ch API.
Handles the complete registration process:
1. Submits JWT to API
2. Processes response
3. Formats result or error message
Args:
jwt_token (str): Signed JWT for registration
Returns:
dict: Registration result containing:
- success: Boolean indicating success
- response: API response data
- error: Error message if failed
Security:
- Uses HTTPS for API communication
- Validates response status
- Handles errors gracefully
"""
try:
response = requests.post(
self.api_url,
json={"jwtEncoded": jwt_token},
headers={'Content-Type': 'application/json'}
)
print(f"\nServer Response Status: {response.status_code}")
print(f"Server Response Body: {response.text}")
if response.status_code in [200, 201]:
return {
'success': True,
'response': response.json()
}
else:
try:
error_json = response.json()
error_msg = error_json.get('error', {}).get(
'message', 'Unknown error'
)
return {
'success': False,
'error': f"Registration failed ({response.status_code}): "
f"{error_msg}",
'response': error_json
}
except json.JSONDecodeError:
return {
'success': False,
'error': f"Registration failed ({response.status_code}): "
f"{response.text}",
'response': response.text
}
except (requests.RequestException, ConnectionError) as e:
return {
'success': False,
'error': f"Request failed: {str(e)}"
}
def main():
"""
Main entry point for DID generation script.
Handles:
1. Command line argument parsing
2. DID generation and registration process
3. Result output and error display
Usage:
python did_generator.py [options]
"""
parser = argparse.ArgumentParser(
description='Generate a DID with admin authorization'
)
parser.add_argument(
'--admin-did',
help='Admin DID (e.g., did:ethr:0x0000...)',
required=False
)
parser.add_argument(
'--api-url',
help='Override API URL',
default=os.getenv('ENDORSER_API_URL', 'https://test-api.endorser.ch/api/v2/claim')
)
args = parser.parse_args()
# Get admin credentials from environment
admin_keypair = {
'did': os.getenv('ADMIN_DID', 'did:ethr:0x0000694B58C2cC69658993A90D3840C560f2F51F'),
'private_key': os.getenv('ADMIN_PRIVATE_KEY', '2b6472c026ec2aa2c4235c994a63868fc9212d18b58f6cbfe861b52e71330f5b')
}
admin_did = args.admin_did
if not admin_did:
admin_did = admin_keypair['did']
print(f"Admin DID: {admin_did}")
print(f"API URL: {args.api_url}")
print('Starting DID Generation...\n')
registrar = DIDRegistration(admin_keypair, args.api_url)
print("Generating new keypair...")
new_keypair = registrar.create_keypair()
print("\nGenerated DID Details:")
print("----------------------")
print(f"DID: {new_keypair['did']}")
print(f"Admin DID: {admin_did}")
print(f"Address: {new_keypair['address']}")
print(f"Private Key: {new_keypair['private_key']}")
print(f"Public Key: {new_keypair['public_key']}\n")
print("Creating JWT...")
jwt_token = registrar.create_jwt(new_keypair['did'])
print('\nSuccessfully generated DID with admin authorization!')
print(f'Registration JWT: {jwt_token[:50]}...')
print("\nAttempting registration...")
result = registrar.register_did(jwt_token)
if result['success']:
print("Registration successful!")
print("Response: {json.dumps(result['response'], indent=2)}")
else:
print("Registration failed!")
print(f"Error: {result['error']}")
if 'response' in result:
print(f"Full response: {json.dumps(result['response'], indent=2)}")
if __name__ == "__main__":
main()

View File

@@ -1,266 +0,0 @@
#!/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

View File

@@ -1,250 +0,0 @@
/**
* DID Generator Script
* @author Matthew Raymer
*
* This script generates and registers Decentralized Identifiers (DIDs) with admin authorization.
* It supports the creation of Ethereum-based DIDs (did:ethr) and handles the complete
* registration flow with the endorser.ch API.
*
* Features:
* - Ethereum keypair generation using ethers.js
* - JWT creation and signing using did-jwt
* - DID registration with admin authorization
* - Detailed error handling and logging
* - Command-line interface for admin DID input
*
* Dependencies:
* did-jwt: For JWT creation and signing
* ethers: For Ethereum account operations
* node-fetch: For API communication
* commander: For CLI argument parsing
* dotenv: For environment variable loading
*
* Usage:
* npm run generate-did -- [options]
*
* Options:
* -a, --admin-did <did> Override default admin DID
* --api-url <url> Override default API endpoint
*
* Environment:
* Default Admin DID: did:ethr:0x0000694B58C2cC69658993A90D3840C560f2F51F
* Default API URL: https://test-api.endorser.ch/api/v2/claim
*/
/// <reference types="node" />
// Add at the top of your file to ignore dom types
import * as didJwt from 'did-jwt';
import { ethers } from 'ethers';
import fetch from 'node-fetch';
import { program } from 'commander';
import * as dotenv from 'dotenv';
import { config } from 'dotenv';
// Load environment variables
config();
const admin_keypair = {
did: process.env.ADMIN_DID || 'did:ethr:0x0000694B58C2cC69658993A90D3840C560f2F51F',
privateKey: process.env.ADMIN_PRIVATE_KEY || '2b6472c026ec2aa2c4235c994a63868fc9212d18b58f6cbfe861b52e71330f5b'
};
// Default values from environment
const DEFAULT_ADMIN_DID = admin_keypair.did;
const DEFAULT_API_URL = process.env.ENDORSER_API_URL || 'https://test-api.endorser.ch/api/v2/claim';
/**
* Result interface for DID creation process
*/
interface DIDCreationResult {
did: string; // The generated DID
privateKey: string; // Private key without 0x prefix
publicKey: string; // Public key with 0x prefix
isValid: boolean; // Validation status
jwt: string; // Signed JWT for registration
}
/**
* Result interface for DID registration attempt
*/
interface RegistrationResult {
success: boolean; // Registration success status
error?: string; // Optional error message
response?: any; // Optional API response data
}
/**
* Creates and validates a new DID with admin authorization
*
* Workflow:
* 1. Generates new Ethereum keypair using ethers.js
* 2. Creates DID from public key in did:ethr format
* 3. Constructs registration claim with:
* - Admin DID as the agent (authorizer)
* - New DID as the participant (being registered)
* 4. Signs claim as JWT using admin's private key
*
* @param adminDid - Administrator DID for authorization
* @returns Promise<DIDCreationResult> - Generated DID details and JWT
*/
async function createAndValidateDID(adminDid: string): Promise<DIDCreationResult> {
console.log('Using admin DID:', adminDid);
// Generate new DID keypair
console.log('Generating new keypair...');
const wallet = ethers.Wallet.createRandom();
const did = `did:ethr:${wallet.address}`;
const privateKey = wallet.privateKey.slice(2);
const publicKey = wallet.publicKey;
// Create registration claim with admin as agent
const registerClaim = {
"@context": "https://schema.org",
"@type": "RegisterAction",
agent: { did: adminDid },
participant: { did: did },
object: "endorser.ch"
};
const vcPayload = {
iat: Math.floor(Date.now() / 1000),
exp: Math.floor(Date.now() / 1000) + 300,
sub: "RegisterAction",
vc: {
"@context": ["https://www.w3.org/2018/credentials/v1"],
type: ["VerifiableCredential"],
credentialSubject: registerClaim
}
};
console.log('\nGenerated DID Details:');
console.log('----------------------');
console.log('DID:', did);
console.log('Admin DID:', adminDid);
console.log('Address:', wallet.address);
console.log('Private Key:', wallet.privateKey);
console.log('Public Key:', wallet.publicKey);
console.log('\nDebug Details:');
console.log('-------------');
console.log('Private Key (hex):', privateKey); // Should be without 0x
console.log('Public Key (hex):', publicKey); // Should be with 0x
console.log('Header:', {
typ: "JWT",
alg: "ES256K"
});
console.log('Payload:', vcPayload);
// Create and sign JWT with admin's key
console.log('\nCreating JWT...');
const signer = didJwt.SimpleSigner(admin_keypair.privateKey); // Use admin's private key
const jwt = await didJwt.createJWT(vcPayload, {
issuer: admin_keypair.did, // Admin DID as issuer
signer: signer
});
console.log('\nJWT Parts:');
const [header, payload, signature] = jwt.split('.');
console.log('Header (base64):', header);
console.log('Payload (base64):', payload);
console.log('Signature (base64):', signature);
return { did, privateKey, publicKey, isValid: true, jwt };
}
/**
* Registers a DID with the endorser.ch API
*
* Workflow:
* 1. Submits JWT to API endpoint
* 2. Processes response (success/error)
* 3. Handles error cases:
* - Network errors
* - API errors (400, 401, etc)
* - Malformed responses
*
* @param jwt - Signed JWT for registration
* @returns Promise<RegistrationResult> - Registration result with response/error
*/
async function registerDID(jwt: string): Promise<RegistrationResult> {
try {
const response = await fetch(DEFAULT_API_URL, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({ jwtEncoded: jwt }),
});
console.log(`\nServer Response Status: ${response.status}`);
const responseText = await response.text();
console.log(`Server Response Body: ${responseText}`);
if (response.ok) {
return {
success: true,
response: JSON.parse(responseText)
};
} else {
try {
const errorJson = JSON.parse(responseText);
const errorMsg = errorJson.error?.message || 'Unknown error';
return {
success: false,
error: `Registration failed (${response.status}): ${errorMsg}`,
response: errorJson
};
} catch {
return {
success: false,
error: `Registration failed (${response.status}): ${responseText}`,
response: responseText
};
}
}
} catch (e) {
return {
success: false,
error: `Request failed: ${e}`
};
}
}
// Command line handling
program
.name('did-generator')
.description('Generate and register a new DID')
.option('-a, --admin-did <did>', 'Admin DID', DEFAULT_ADMIN_DID)
.option('--api-url <url>', 'Override API URL', DEFAULT_API_URL)
.parse();
const options = program.opts();
const adminDid = options.adminDid; // Use the option instead of args
const apiUrl = options.apiUrl;
console.log('Starting DID Generation...\n');
console.log('Using admin DID:', adminDid); // Log the admin DID being used
(async () => {
try {
const result = await createAndValidateDID(adminDid);
console.log('\nSuccessfully generated DID with admin authorization!');
console.log('Registration JWT:', result.jwt.substring(0, 50) + '...');
console.log('\nAttempting registration...');
const registrationResult = await registerDID(result.jwt);
if (registrationResult.success) {
console.log('Registration successful!');
console.log('Response:', JSON.stringify(registrationResult.response, null, 2));
} else {
console.log('Registration failed!');
console.log('Error:', registrationResult.error);
if (registrationResult.response) {
console.log('Full response:', JSON.stringify(registrationResult.response, null, 2));
}
}
} catch (error) {
console.error('\nError:', error);
process.exit(1);
}
})();

View File

@@ -1,284 +0,0 @@
#!/bin/bash
# DID Visibility Check Script
# @author Matthew Raymer
#
# This script checks visibility permissions for DIDs within the endorser.ch system.
# It creates a signed JWT using admin credentials and queries the visibility API.
#
# Features:
# - JWT creation and signing using ES256K-R
# - DID visibility checking
# - Environment variable support
# - Debug logging
# - Command line argument parsing
#
# Usage:
# ./dids_seen.sh [-d did_to_check]
# DEBUG=1 ./dids_seen.sh # For debug output
#
# Environment Variables:
# ADMIN_DID - Admin DID for authorization
# ADMIN_PRIVATE_KEY - Private key for signing
# ENDORSER_API_URL - API endpoint (defaults to test)
# DEBUG - Enable debug logging when set to 1
# Enhanced debug logging
debug_log() {
if [ "${DEBUG:-0}" = "1" ]; then
echo "DEBUG: $*" >&2
fi
}
# Parse command line arguments
# -d: Specific DID to check visibility for
CHECK_DID=""
while getopts "d:" opt; do
case $opt in
d) CHECK_DID="$OPTARG" ;;
\?) echo "Usage: $0 [-d did_to_check]" >&2; exit 1 ;;
esac
done
# Load environment variables from .env file if present
# Supports:
# - ADMIN_DID
# - ADMIN_PRIVATE_KEY
# - ENDORSER_API_URL
if [ -f .env ]; then
export $(cat .env | grep -v '^#' | xargs)
fi
# Default values for required parameters
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/report/whichDidsICanSee"}
# Create JWT payload with:
# - Issuer (iss)
# - Subject (sub)
# - Issued At (iat)
# - Expiration (exp)
now=$(date +%s)
exp=$((now + 86400)) # 24 hours from now
payload=$(jq -n \
--arg iss "$ADMIN_DID" \
--arg sub "$ADMIN_DID" \
--arg iat "$now" \
--arg exp "$exp" \
'{
iss: $iss,
sub: $sub,
iat: ($iat | tonumber),
exp: ($exp | tonumber)
}')
# Base64url encode header and payload
# Header specifies ES256K-R algorithm for Ethereum compatibility
header='{"alg":"ES256K-R","typ":"JWT"}'
header_b64=$(echo -n "$header" | base64 -w 0 | tr '/+' '_-' | tr -d '=')
payload_b64=$(echo -n "$payload" | base64 -w 0 | tr '/+' '_-' | tr -d '=')
# Create message to sign (header.payload)
message="$header_b64.$payload_b64"
# Add debug points
debug_log "Creating JWT with:"
debug_log "ADMIN_DID: $ADMIN_DID"
debug_log "API_URL: $API_URL"
debug_log "Payload: $payload"
debug_log "Header base64: $header_b64"
debug_log "Payload base64: $payload_b64"
debug_log "Message to sign: $message"
# Create temporary directory for key operations
# Uses trap to ensure cleanup on exit
TMPDIR=$(mktemp -d)
trap 'rm -rf "$TMPDIR"' EXIT
# Create private key PEM file
# Converts raw private key to PEM format for OpenSSL
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 for signing
echo -n "$message" > "$TMPDIR/message.txt"
# Sign message and get DER format signature
openssl dgst -sha256 -sign "$TMPDIR/private.pem" "$TMPDIR/message.txt" > "$TMPDIR/signature.der"
# Convert DER signature to hex
der_sig=$(xxd -p -c 256 "$TMPDIR/signature.der")
# Parse DER structure
# Extracts R and S values from signature
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
r_len="${BASH_REMATCH[2]}"
r_val="${BASH_REMATCH[3]}"
s_len="${BASH_REMATCH[4]}"
s_val="${BASH_REMATCH[5]}"
debug_log "Raw signature values:"
debug_log " R (${#r_val} chars): $r_val"
debug_log " S (${#s_val} chars): $s_val"
# Convert lengths to decimal
r_len_dec=$((16#$r_len))
s_len_dec=$((16#$s_len))
# Handle R value padding
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 padding
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
# Ensure both values are exactly 64 characters
r_val=$(printf "%064s" "$r_val" | tr ' ' '0')
s_val=$(printf "%064s" "$s_val" | tr ' ' '0')
debug_log "Normalized values:"
debug_log " R (${#r_val} chars): $r_val"
debug_log " S (${#s_val} chars): $s_val"
# Create final signature
concat_sig="${r_val}${s_val}"
# Debug the DER parsing
debug_log "DER Signature Analysis:"
debug_log " Full DER: $der_sig"
debug_log " Sequence length: ${BASH_REMATCH[1]}"
debug_log " R length: $r_len ($r_len_dec bytes)"
debug_log " S length: $s_len ($s_len_dec bytes)"
# Debug signature components
debug_log "Signature components:"
debug_log " R value: $r_val (length: ${#r_val})"
debug_log " S value: $s_val (length: ${#s_val})"
debug_log " Concatenated: $concat_sig (length: ${#concat_sig})"
# Try both normal and high-S value signatures
s_val_alt=""
if [ $s_len_dec -gt 32 ]; then
# Store alternative S value
s_val_alt="$s_val"
# Calculate N - s for high-S values
n="FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141"
# Use Python for hex math to avoid bc issues
normalized_s=$(python3 -c "
n = int('$n', 16)
s = int('${s_val}', 16)
result = hex(n - s)[2:].zfill(64)
print(result.lower())
" 2>/dev/null || echo "ERROR")
if [ "$normalized_s" = "ERROR" ]; then
debug_log " Failed to normalize S value"
# Keep original s_val if normalization fails
s_val_alt=""
else
s_val=$(printf "%064s" "$normalized_s" | tr ' ' '0' | tr '[:upper:]' '[:lower:]')
debug_log " Normalized S value: $s_val"
fi
fi
# Calculate recovery bit (v)
# Try each possible recovery value (0-3) until we find one that works
for v in {0..3}; do
# Try both S values if we have an alternative
s_values=("$s_val")
if [ -n "$s_val_alt" ]; then
s_values+=("$s_val_alt")
fi
for current_s in "${s_values[@]}"; do
concat_sig="${r_val}${current_s}"
debug_log "Trying with S value: $current_s"
recovery_sig=$(printf "%s%02x" "$concat_sig" "$v")
debug_log "Recovery attempt $v:"
debug_log " Concatenated signature: $concat_sig"
debug_log " Recovery signature: $recovery_sig"
debug_log " Recovery signature length: ${#recovery_sig}"
# Ensure signature is exactly 65 bytes (130 hex chars)
if [ ${#recovery_sig} -ne 130 ]; then
debug_log " Invalid signature length: ${#recovery_sig}, expected 130"
continue
fi
# Convert hex to binary, then to base64url
signature=$(echo -n "$recovery_sig" | xxd -r -p 2>/dev/null | base64 -w 0 | tr '/+' '_-' | tr -d '=')
debug_log " Base64URL signature: $signature"
# Create JWT with this signature
JWT="$message.$signature"
debug_log " Testing JWT: $JWT"
# Test the JWT against the API
response=$(curl -s -X GET "$API_URL" \
-H "Content-Type: application/json" \
-H "Authorization: Bearer $JWT")
debug_log " API Response: $response"
# Check if the JWT worked
if ! echo "$response" | grep -q "JWT.*failed"; then
echo "JWT Token:"
echo "$JWT"
debug_log "Success with v=$v and S=${current_s:0:8}..."
echo
echo "Export command:"
echo "export TOKEN='$JWT'"
echo
if [ -n "$CHECK_DID" ]; then
# Check if specific DID is in the list
if echo "$response" | jq -e --arg did "$CHECK_DID" 'contains([$did])' > /dev/null; then
echo "✅ DID $CHECK_DID is in the list"
exit 0
else
echo "❌ DID $CHECK_DID is not in the list"
echo "Attempting to add visibility..."
# Request visibility
visibility_response=$(curl -s -X POST \
'http://test-api.endorser.ch/api/report/canSeeMe' \
-H "Authorization: Bearer $JWT" \
-H "Content-Type: application/json" \
-d "{\"did\": \"$CHECK_DID\"}")
if echo "$visibility_response" | grep -q "error"; then
echo "❌ Failed to add visibility:"
echo "$visibility_response" | jq '.' --indent 2
exit 1
else
echo "✅ Successfully requested visibility. Please try checking again."
exit 0
fi
fi
else
# Show full list of visible DIDs
echo "$response" | jq '.' --indent 2
fi
exit 0
fi
done
done
echo "Error: Could not find valid recovery bit"
exit 1
else
echo "Error: Invalid DER signature format"
echo "DER: $der_sig"
exit 1
fi

View File

@@ -1,175 +0,0 @@
#!/bin/bash
# Source the environment with DIDs
if [ ! -f .generated/test-env.sh ]; then
echo "Error: No test environment found. Run run-deeplink-tests.sh first"
exit 1
fi
source .generated/test-env.sh
# Verify we have the required DIDs
if [ -z "$CONTACT1_DID" ] || [ -z "$CONTACT1_KEY" ]; then
echo "Error: Contact1 DID info not found in environment"
exit 1
fi
# Use CONTACT1 as the issuer since we know it's registered
REGISTERED_DID="$CONTACT1_DID"
REGISTERED_KEY="$CONTACT1_KEY"
# Default API URL (can be overridden by environment)
API_URL=${ENDORSER_API_URL:-"https://test-api.endorser.ch/api/v2/claim"}
# Function to sign a message using ES256K
sign_message() {
local message="$1"
local private_key="$2"
local tmpdir
# Create temporary directory
tmpdir=$(mktemp -d)
trap 'rm -rf "$tmpdir"' EXIT
# Debug output
echo "Signing message: $message" >&2
echo "Using private key: $private_key" >&2
# Hash the message with SHA-256
echo -n "$message" | openssl dgst -sha256 -binary > "$tmpdir/message.hash"
# Sign the hash directly using the private key
# This avoids OpenSSL's PEM format issues with secp256k1
python3 -c "
import sys
from eth_keys import keys
import hashlib
# Read message hash
with open('$tmpdir/message.hash', 'rb') as f:
message_hash = f.read()
# Create private key object
private_key_bytes = bytes.fromhex('$private_key')
private_key = keys.PrivateKey(private_key_bytes)
# Sign the message hash
signature = private_key.sign_msg_hash(message_hash)
# Output R and S values as hex
print(f'{hex(signature.r)[2:]:0>64}')
print(f'{hex(signature.s)[2:]:0>64}')
" > "$tmpdir/signature.txt"
# Read R and S values
{ read -r r_val; read -r s_val; } < "$tmpdir/signature.txt"
# Debug R and S values
echo "R value: $r_val" >&2
echo "S value: $s_val" >&2
# Concatenate R+S and convert to base64url
echo -n "${r_val}${s_val}" | xxd -r -p | base64 -w 0 | tr '/+' '_-' | tr -d '='
}
# Create a test claim payload
create_claim_payload() {
local issuer_did="$1"
# Create a test claim matching the app's structure
cat << EOF
{
"@context": "https://schema.org",
"@type": "TestClaim",
"identifier": "test-claim-$(date +%s)",
"name": "Test Claim",
"description": "Generated test claim",
"agent": {
"did": "$issuer_did"
}
}
EOF
}
# Create and sign JWT
create_jwt() {
local claim="$1"
local issuer_did="$2"
local private_key="$3"
# Create header and payload matching the app's structure
local header='{"typ":"JWT","alg":"ES256K"}'
local payload="{\"iat\":$(date +%s),\"exp\":$(($(date +%s) + 300)),\"iss\":\"$issuer_did\",\"vc\":{\"@context\":[\"https://www.w3.org/2018/credentials/v1\"],\"type\":[\"VerifiableCredential\"],\"credentialSubject\":$claim}}"
# Base64url encode header and payload
local header_b64=$(echo -n "$header" | base64 -w 0 | tr '/+' '_-' | tr -d '=')
local payload_b64=$(echo -n "$payload" | base64 -w 0 | tr '/+' '_-' | tr -d '=')
# Create message to sign
local message="$header_b64.$payload_b64"
# Sign message using eth_keys (same as did-jwt library)
local signature=$(python3 -c "
from eth_keys import keys
import hashlib
# Create private key object
private_key_bytes = bytes.fromhex('$private_key')
private_key = keys.PrivateKey(private_key_bytes)
# Hash the message
message_hash = hashlib.sha256('$message'.encode()).digest()
# Sign using ES256K
signature = private_key.sign_msg_hash(message_hash)
# Format as R+S concatenated and base64url encoded
import base64
signature_bytes = signature.r.to_bytes(32, 'big') + signature.s.to_bytes(32, 'big')
print(base64.urlsafe_b64encode(signature_bytes).decode().rstrip('='))
")
# Return complete JWT
echo "$message.$signature"
}
# Function to register claim with API
register_claim() {
local jwt="$1"
echo "Registering claim with API..."
response=$(curl -s -X POST "$API_URL" \
-H "Content-Type: application/json" \
-d "{\"jwtEncoded\":\"$jwt\"}")
# Check response
if echo "$response" | jq -e '.success' >/dev/null 2>&1; then
echo "Registration successful!"
echo "Response:"
echo "$response" | jq '.'
return 0
else
echo "Registration failed!"
echo "Error: $(echo "$response" | jq -r '.error.message // empty')"
echo "Full response:"
echo "$response" | jq '.' || echo "$response"
return 1
fi
}
# Main execution
echo "Generating test claim..."
# Create claim payload
claim=$(create_claim_payload "$REGISTERED_DID")
echo "Claim payload:"
echo "$claim" | jq '.'
# Create and sign JWT
echo "Generating signed JWT..."
jwt=$(create_jwt "$claim" "$REGISTERED_DID" "$REGISTERED_KEY")
# Output just the JWT for piping
echo "$jwt"
# Register the claim
register_claim "$jwt"

View File

@@ -1 +0,0 @@
usage gas they pyramid walnut mammal absorb major crystal nurse element congress assist panic bomb entire area slogan film educate decrease buddy describe finish

View File

@@ -14,7 +14,7 @@ YELLOW='\033[1;33m'
NC='\033[0m'
# Parse command line arguments
TIMEOUT=5
TIMEOUT=10
TEST_MODE="execute" # Default to execute mode
while getopts "t:p" opt; do

View File

@@ -1,17 +0,0 @@
#!/bin/bash
# Helper script for secp256k1 signing using pure shell commands
PRIVATE_KEY_FILE="$1"
MESSAGE_HASH_FILE="$2"
# Load private key and message hash
PRIVATE_KEY=$(cat "$PRIVATE_KEY_FILE" | xxd -p -c 64)
MESSAGE_HASH=$(cat "$MESSAGE_HASH_FILE" | xxd -p -c 32)
# Use secp256k1 library through Python (as a last resort)
python3 -c "
from coincurve import PrivateKey
private_key = PrivateKey(bytes.fromhex('$PRIVATE_KEY'))
signature = private_key.sign(bytes.fromhex('$MESSAGE_HASH'), hasher=None)
print(signature.hex())
" | xxd -r -p | base64 -w 0 | tr '/+' '_-' | tr -d '='

View File

@@ -1,130 +0,0 @@
#!/bin/bash
# Verify required environment variables are set
required_vars=(
"CONTACT1_DID"
"CONTACT1_KEY"
"CONTACT2_DID"
"CONTACT2_KEY"
"ISSUER_DID"
"ISSUER_KEY"
"CONTACTS_JSON"
)
for var in "${required_vars[@]}"; do
if [ -z "${!var}" ]; then
echo "Error: $var is not set"
echo "Please run: source .generated/test-env.sh"
exit 1
fi
done
# Color definitions
BLUE='\033[0;34m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
PURPLE='\033[0;35m'
CYAN='\033[0;36m'
NC='\033[0m' # No Color
BOLD='\033[1m'
# Parse command line arguments
TIMEOUT=5
ALL_TESTS=false
TEST_MODE=${TEST_MODE:-execute} # Default to execute mode
while getopts "t:a" opt; do
case $opt in
t) TIMEOUT=$OPTARG ;;
a) ALL_TESTS=true ;;
\?) echo "Invalid option: -$OPTARG" >&2; exit 1 ;;
esac
done
# Check for adb and connected devices
if [ "$TEST_MODE" = "execute" ]; then
if ! command -v adb >/dev/null 2>&1; then
echo "Warning: adb not found, switching to print mode"
TEST_MODE=print
elif [ -z "$(adb devices | grep -v List | grep device)" ]; then
echo "Warning: no devices/emulators found, switching to print mode"
TEST_MODE=print
fi
fi
# Function to encode URL parameters
urlencode() {
local string="${1}"
local strlen=${#string}
local encoded=""
local pos c o
for (( pos=0 ; pos<strlen ; pos++ )); do
c=${string:$pos:1}
case "$c" in
[-_.~a-zA-Z0-9] ) o="${c}" ;;
* ) printf -v o '%%%02x' "'$c"
esac
encoded+="${o}"
done
echo "${encoded}"
}
# Function to print section header
print_header() {
echo -e "\n${BOLD}${1}${NC}"
echo -e "${BOLD}$(printf '=%.0s' {1..50})${NC}\n"
}
# Function to handle deep links
handle_deeplink() {
local url="$1"
local color="$2"
local encoded_url=$(urlencode "$url")
if [ "$TEST_MODE" = "print" ]; then
echo -e "${color}Deep Link URL:${NC}"
echo -e "${color}$url${NC}"
echo -e "${color}Encoded URL:${NC}"
echo -e "${color}$encoded_url${NC}"
echo "---"
else
echo -e "${color}Opening: $url${NC}"
adb shell am start -a android.intent.action.VIEW -d "$encoded_url"
sleep "$TIMEOUT"
fi
}
# Generate a test JWT for claims
TEST_JWT=$(./test-scripts/did_generator.sh | grep "Created JWT:" | cut -d' ' -f3)
echo -e "${BOLD}Running deep link tests (mode: $TEST_MODE)...${NC}"
# 1. Basic routes
print_header "Basic Routes"
handle_deeplink "timesafari://claim-cert/$TEST_JWT" $BLUE
handle_deeplink "timesafari://claim-add-raw/$TEST_JWT?claim=$(urlencode '{"type":"test"}')&claimJwtId=$TEST_JWT" $BLUE
# 2. Contact import routes
print_header "Contact Import Routes"
handle_deeplink "timesafari://contacts/import?contacts=$(urlencode "$CONTACTS_JSON")" $GREEN
handle_deeplink "timesafari://contacts" $GREEN
# 3. Contact management routes
print_header "Contact Management Routes"
handle_deeplink "timesafari://contact-edit?did=$CONTACT1_DID" $YELLOW
handle_deeplink "timesafari://contact-edit?did=$CONTACT2_DID" $YELLOW
if [ "$ALL_TESTS" = true ]; then
# 4. Claims and verification routes
print_header "Claims and Verification Routes"
handle_deeplink "timesafari://verify?issuer=$ISSUER_DID" $PURPLE
handle_deeplink "timesafari://claims?contact=$CONTACT1_DID" $PURPLE
# 5. Project routes
print_header "Project Routes"
handle_deeplink "timesafari://projects" $CYAN
handle_deeplink "timesafari://project-edit?id=test-project" $CYAN
fi
echo -e "\n${BOLD}Deep link tests completed${NC}"