diff --git a/.env.example b/.env.example new file mode 100644 index 0000000..983c4b6 --- /dev/null +++ b/.env.example @@ -0,0 +1,6 @@ +# Admin DID credentials +ADMIN_DID=did:ethr:0x0000694B58C2cC69658993A90D3840C560f2F51F +ADMIN_PRIVATE_KEY=2b6472c026ec2aa2c4235c994a63868fc9212d18b58f6cbfe861b52e71330f5b + +# API Configuration +ENDORSER_API_URL=https://test-api.endorser.ch/api/v2/claim \ No newline at end of file diff --git a/.gitignore b/.gitignore index edc9e9f..c61f208 100644 --- a/.gitignore +++ b/.gitignore @@ -41,4 +41,5 @@ playwright-tests test-playwright dist-electron-packages ios -.ruby-version \ No newline at end of file +.ruby-version ++.env \ No newline at end of file diff --git a/package.json b/package.json index 7376495..7cce50c 100644 --- a/package.json +++ b/package.json @@ -104,7 +104,8 @@ "vue-qrcode-reader": "^5.5.3", "vue-router": "^4.5.0", "web-did-resolver": "^2.0.27", - "zod": "^3.24.2" + "zod": "^3.24.2", + "dotenv": "^16.0.3" }, "devDependencies": { "@playwright/test": "^1.45.2", diff --git a/test-scripts/did_generator.py b/test-scripts/did_generator.py index e17cf1d..dd0ab54 100644 --- a/test-scripts/did_generator.py +++ b/test-scripts/did_generator.py @@ -22,6 +22,8 @@ Dependencies: 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] @@ -46,6 +48,11 @@ import argparse import secrets import hashlib from pathlib import Path +from dotenv import load_dotenv +import os + +# Load environment variables +load_dotenv() class DIDRegistration: """ @@ -305,12 +312,14 @@ def main(): parser.add_argument( '--api-url', help='Override API URL', - default="https://test-api.endorser.ch/api/v2/claim" + default=os.getenv('ENDORSER_API_URL', 'https://test-api.endorser.ch/api/v2/claim') ) args = parser.parse_args() - admin_keypair = { - 'did': 'did:ethr:0x0000694B58C2cC69658993A90D3840C560f2F51F', - 'private_key': '2b6472c026ec2aa2c4235c994a63868fc9212d18b58f6cbfe861b52e71330f5b' + + # 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 diff --git a/test-scripts/did_generator.sh b/test-scripts/did_generator.sh new file mode 100755 index 0000000..c22a1b0 --- /dev/null +++ b/test-scripts/did_generator.sh @@ -0,0 +1,189 @@ +#!/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"} + +# Function to generate a new keypair +generate_keypair() { + echo "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" + + echo "Generated DID Details:" + echo "----------------------" + echo "DID: $DID" + echo "Address: 0x$ADDRESS" + echo "Private Key: $PRIVATE_KEY" + echo "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 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" + + # 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 R+S format + der_sig=$(xxd -p -c 256 "$TMPDIR/signature.der") + + # Debug the full DER signature + echo "Debug - Full DER signature:" + echo "$der_sig" + + # Parse DER structure - more robust version + 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]}" + + echo "Debug - DER parsing:" + echo "Sequence length: $seq_len" + echo "R length: $r_len" + echo "R value: $r_val" + echo "S length: $s_len" + echo "S value: $s_val" + + # Remove leading zeros if present + r_val=${r_val#"00"} + s_val=${s_val#"00"} + + # Pad R and S to 32 bytes each + r_val=$(printf "%064s" "$r_val" | tr ' ' '0') + s_val=$(printf "%064s" "$s_val" | tr ' ' '0') + + # Concatenate R+S and convert to base64url + signature=$(echo -n "${r_val}${s_val}" | xxd -r -p | base64 -w 0 | tr '/+' '_-' | tr -d '=') + else + echo "Error: Invalid DER signature format" + echo "DER: $der_sig" + return 1 + fi + + # Create final JWT + JWT="$message.$signature" + + echo -e "\nCreated JWT: ${JWT:0:50}..." + export JWT + return 0 +} + +# Function to register DID +register_did() { + echo "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 + echo "Registration successful!" + echo "Response:" + echo "$response" | jq '.' + else + echo "Registration failed!" + echo "Error: $(echo "$response" | jq -r '.error.message // empty')" + echo "Full response:" + echo "$response" | jq '.' || echo "$response" + fi +} + +# Main execution +echo "Starting DID Generation..." +echo "Using admin DID: $ADMIN_DID" +echo "API URL: $API_URL" + +generate_keypair && \ +create_jwt && \ +register_did + +exit $? \ No newline at end of file diff --git a/test-scripts/did_generator.ts b/test-scripts/did_generator.ts index 7639913..51b6bf8 100644 --- a/test-scripts/did_generator.ts +++ b/test-scripts/did_generator.ts @@ -18,6 +18,7 @@ * 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] @@ -37,16 +38,20 @@ 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'; -const admin_keypair = { - did: 'did:ethr:0x0000694B58C2cC69658993A90D3840C560f2F51F', - privateKey: '2b6472c026ec2aa2c4235c994a63868fc9212d18b58f6cbfe861b52e71330f5b' -} -// Default admin DID -const DEFAULT_ADMIN_DID = admin_keypair.did; +// Load environment variables +config(); -// Add constant for default API URL -const DEFAULT_API_URL = 'https://test-api.endorser.ch/api/v2/claim'; +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 diff --git a/test-scripts/requirements.txt b/test-scripts/requirements.txt index ced4027..7b63c58 100644 --- a/test-scripts/requirements.txt +++ b/test-scripts/requirements.txt @@ -2,4 +2,5 @@ eth-account>=0.8.0 eth-keys>=0.4.0 PyJWT>=2.8.0 requests>=2.31.0 -cryptography>=41.0.0 \ No newline at end of file +cryptography>=41.0.0 +python-dotenv==1.0.0 \ No newline at end of file