fix: WIP: Update test scripts for DID verification and claim generation

- Add check-did.sh to verify DID registration using admin JWT auth
- Fix JWT signing in generate-test-claim.sh to match uport-credentials format
- Clean up DID extraction in run-deeplink-tests.sh
- Add proper error handling and response parsing

The changes improve test script reliability by:
1. Using consistent JWT signing across scripts
2. Adding ability to verify DID registration status
3. Simplifying DID info extraction
4. Adding better error messages and debug output
This commit is contained in:
Matthew Raymer
2025-03-04 10:20:14 +00:00
parent 69b4b899c9
commit bc971056e1
3 changed files with 167 additions and 73 deletions

59
test-scripts/check-did.sh Executable file
View File

@@ -0,0 +1,59 @@
#!/bin/bash
# Usage: ./check-did.sh [did]
# If no DID provided, lists all visible DIDs
API_URL=${ENDORSER_API_URL:-"https://test-api.endorser.ch/api/v2/claim"}
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

@@ -7,6 +7,19 @@ if [ ! -f .generated/test-env.sh ]; then
fi fi
source .generated/test-env.sh 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 # Function to sign a message using ES256K
sign_message() { sign_message() {
local message="$1" local message="$1"
@@ -17,62 +30,62 @@ sign_message() {
tmpdir=$(mktemp -d) tmpdir=$(mktemp -d)
trap 'rm -rf "$tmpdir"' EXIT trap 'rm -rf "$tmpdir"' EXIT
# Create private key PEM file # Debug output
echo "-----BEGIN EC PRIVATE KEY-----" > "$tmpdir/private.pem" echo "Signing message: $message" >&2
( echo "Using private key: $private_key" >&2
echo "302e0201010420" # Private key header
echo -n "$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 # Hash the message with SHA-256
echo -n "$message" > "$tmpdir/message.txt" echo -n "$message" | openssl dgst -sha256 -binary > "$tmpdir/message.hash"
# Sign message and get DER format signature # Sign the hash directly using the private key
openssl dgst -sha256 -sign "$tmpdir/private.pem" "$tmpdir/message.txt" > "$tmpdir/signature.der" # This avoids OpenSSL's PEM format issues with secp256k1
python3 -c "
import sys
from eth_keys import keys
import hashlib
# Convert DER signature to R+S format # Read message hash
der_sig=$(xxd -p -c 256 "$tmpdir/signature.der") with open('$tmpdir/message.hash', 'rb') as f:
message_hash = f.read()
# Parse DER structure # Create private key object
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 private_key_bytes = bytes.fromhex('$private_key')
r_val="${BASH_REMATCH[3]}" private_key = keys.PrivateKey(private_key_bytes)
s_val="${BASH_REMATCH[5]}"
# Remove leading zeros # Sign the message hash
r_val=${r_val#"00"} signature = private_key.sign_msg_hash(message_hash)
s_val=${s_val#"00"}
# Pad to 64 chars # Output R and S values as hex
r_val=$(printf "%064s" "$r_val" | tr ' ' '0') print(f'{hex(signature.r)[2:]:0>64}')
s_val=$(printf "%064s" "$s_val" | tr ' ' '0') print(f'{hex(signature.s)[2:]:0>64}')
" > "$tmpdir/signature.txt"
# Take last 64 chars if longer # Read R and S values
r_val=${r_val:(-64)} { read -r r_val; read -r s_val; } < "$tmpdir/signature.txt"
s_val=${s_val:(-64)}
# Concatenate R+S and convert to base64url # Debug R and S values
echo -n "${r_val}${s_val}" | xxd -r -p | base64 -w 0 | tr '/+' '_-' | tr -d '=' echo "R value: $r_val" >&2
else echo "S value: $s_val" >&2
echo "Error: Invalid DER signature format" >&2
return 1 # Concatenate R+S and convert to base64url
fi echo -n "${r_val}${s_val}" | xxd -r -p | base64 -w 0 | tr '/+' '_-' | tr -d '='
} }
# Create a test claim payload # Create a test claim payload
create_claim_payload() { create_claim_payload() {
local issuer_did="$1" local issuer_did="$1"
local subject_did="$2"
# Create registration claim with issuer as agent # Create a test claim matching the app's structure
cat << EOF cat << EOF
{ {
"@context": "https://schema.org", "@context": "https://schema.org",
"@type": "TestClaim", "@type": "TestClaim",
"agent": { "did": "$issuer_did" }, "identifier": "test-claim-$(date +%s)",
"participant": { "did": "$subject_did" }, "name": "Test Claim",
"object": "test.endorser.ch" "description": "Generated test claim",
"agent": {
"did": "$issuer_did"
}
} }
EOF EOF
} }
@@ -83,9 +96,9 @@ create_jwt() {
local issuer_did="$2" local issuer_did="$2"
local private_key="$3" local private_key="$3"
# Create header and payload # Create header and payload matching the app's structure
local header='{"typ":"JWT","alg":"ES256K"}' local header='{"typ":"JWT","alg":"ES256K"}'
local payload="{\"iat\":$(date +%s),\"exp\":$(($(date +%s) + 300)),\"sub\":\"TestClaim\",\"vc\":{\"@context\":[\"https://www.w3.org/2018/credentials/v1\"],\"type\":[\"VerifiableCredential\"],\"credentialSubject\":$claim},\"iss\":\"$issuer_did\"}" 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 # Base64url encode header and payload
local header_b64=$(echo -n "$header" | base64 -w 0 | tr '/+' '_-' | tr -d '=') local header_b64=$(echo -n "$header" | base64 -w 0 | tr '/+' '_-' | tr -d '=')
@@ -94,24 +107,69 @@ create_jwt() {
# Create message to sign # Create message to sign
local message="$header_b64.$payload_b64" local message="$header_b64.$payload_b64"
# Sign message # Sign message using eth_keys (same as did-jwt library)
local signature=$(sign_message "$message" "$private_key") 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 # Return complete JWT
echo "$message.$signature" 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 # Main execution
echo "Generating test claim..." echo "Generating test claim..."
# Create claim payload # Create claim payload
claim=$(create_claim_payload "$ISSUER_DID" "$CONTACT1_DID") claim=$(create_claim_payload "$REGISTERED_DID")
echo "Claim payload:" echo "Claim payload:"
echo "$claim" | jq '.' echo "$claim" | jq '.'
# Create and sign JWT # Create and sign JWT
echo "Generating signed JWT..." echo "Generating signed JWT..."
jwt=$(create_jwt "$claim" "$ISSUER_DID" "$ISSUER_KEY") jwt=$(create_jwt "$claim" "$REGISTERED_DID" "$REGISTERED_KEY")
echo -e "\nGenerated JWT: ${jwt:0:50}..." # Output just the JWT for piping
echo "$jwt" # Output just the JWT for piping echo "$jwt"
# Register the claim
register_claim "$jwt"

View File

@@ -40,37 +40,14 @@ if [ -f .generated/test-env.sh ] && [ "$GENERATE_ONLY" = false ]; then
else else
# Function to extract DID info from did_generator.sh output # Function to extract DID info from did_generator.sh output
extract_did_info() { extract_did_info() {
local output local output="$1"
# Read all input into output variable
output=$(cat)
# Debug the input
echo "Parsing output:" >&2
echo "$output" >&2
# Extract values using more robust patterns
local did=$(echo "$output" | grep "^DID: " | cut -d' ' -f2-) local did=$(echo "$output" | grep "^DID: " | cut -d' ' -f2-)
local private_key=$(echo "$output" | grep "^Private Key: " | cut -d' ' -f3-) local private_key=$(echo "$output" | grep "^Private Key: " | cut -d' ' -f3-)
printf '%s\n' "{\"did\":\"$did\",\"privateKey\":\"$private_key\"}"
# Debug the extracted values
echo "Found DID: $did" >&2
echo "Found Key: $private_key" >&2
# Output JSON only if both values were found
if [ -n "$did" ] && [ -n "$private_key" ]; then
printf '%s\n' "{\"did\":\"$did\",\"privateKey\":\"$private_key\"}"
else
echo "Error: Could not extract DID info from output" >&2
return 1
fi
} }
echo "Generating first contact DID..." echo "Generating first contact DID..."
CONTACT1_INFO=$(./test-scripts/did_generator.sh | tee /dev/stderr | extract_did_info) CONTACT1_INFO=$(./test-scripts/did_generator.sh 2>&1 | tee /dev/stderr | extract_did_info)
if [ $? -ne 0 ]; then
echo "Failed to generate first contact DID"
exit 1
fi
CONTACT1_DID=$(echo "$CONTACT1_INFO" | jq -r .did) CONTACT1_DID=$(echo "$CONTACT1_INFO" | jq -r .did)
CONTACT1_KEY=$(echo "$CONTACT1_INFO" | jq -r .privateKey) CONTACT1_KEY=$(echo "$CONTACT1_INFO" | jq -r .privateKey)
echo "Extracted DID: $CONTACT1_DID" echo "Extracted DID: $CONTACT1_DID"