A community based topic aggregation platform built on atproto

fix: getting did from key

+187
+187
scripts/derive-did-from-key.sh
··· 1 + #!/bin/bash 2 + # Derive public key from existing PDS_ROTATION_KEY and create did.json 3 + # 4 + # This script takes your existing private key and derives the public key from it. 5 + # Use this if you already have a PDS running with a rotation key but need to 6 + # create/fix the did.json file. 7 + # 8 + # Usage: ./scripts/derive-did-from-key.sh 9 + 10 + set -e 11 + 12 + SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" 13 + PROJECT_DIR="$(dirname "$SCRIPT_DIR")" 14 + OUTPUT_DIR="$PROJECT_DIR/static/.well-known" 15 + 16 + # Colors 17 + GREEN='\033[0;32m' 18 + YELLOW='\033[1;33m' 19 + RED='\033[0;31m' 20 + NC='\033[0m' 21 + 22 + log() { echo -e "${GREEN}[DERIVE]${NC} $1"; } 23 + warn() { echo -e "${YELLOW}[WARN]${NC} $1"; } 24 + error() { echo -e "${RED}[ERROR]${NC} $1"; exit 1; } 25 + 26 + # Check for required tools 27 + if ! command -v openssl &> /dev/null; then 28 + error "openssl is required but not installed" 29 + fi 30 + 31 + if ! command -v python3 &> /dev/null; then 32 + error "python3 is required for base58 encoding" 33 + fi 34 + 35 + # Check for base58 library 36 + if ! python3 -c "import base58" 2>/dev/null; then 37 + warn "Installing base58 Python library..." 38 + pip3 install base58 || error "Failed to install base58. Run: pip3 install base58" 39 + fi 40 + 41 + # Load environment to get the existing key 42 + if [ -f "$PROJECT_DIR/.env.prod" ]; then 43 + source "$PROJECT_DIR/.env.prod" 44 + elif [ -f "$PROJECT_DIR/.env" ]; then 45 + source "$PROJECT_DIR/.env" 46 + else 47 + error "No .env.prod or .env file found" 48 + fi 49 + 50 + if [ -z "$PDS_ROTATION_KEY" ]; then 51 + error "PDS_ROTATION_KEY not found in environment" 52 + fi 53 + 54 + # Validate key format (should be 64 hex chars) 55 + if [[ ! "$PDS_ROTATION_KEY" =~ ^[0-9a-fA-F]{64}$ ]]; then 56 + error "PDS_ROTATION_KEY is not a valid 64-character hex string" 57 + fi 58 + 59 + log "Deriving public key from existing PDS_ROTATION_KEY..." 60 + 61 + # Create a temporary PEM file from the hex private key 62 + TEMP_DIR=$(mktemp -d) 63 + PRIVATE_KEY_HEX="$PDS_ROTATION_KEY" 64 + 65 + # Convert hex private key to PEM format 66 + # secp256k1 curve OID: 1.3.132.0.10 67 + python3 > "$TEMP_DIR/private.pem" << EOF 68 + import binascii 69 + 70 + # Private key in hex 71 + priv_hex = "$PRIVATE_KEY_HEX" 72 + priv_bytes = binascii.unhexlify(priv_hex) 73 + 74 + # secp256k1 OID 75 + oid = bytes([0x06, 0x05, 0x2b, 0x81, 0x04, 0x00, 0x0a]) 76 + 77 + # Build the EC private key structure 78 + # SEQUENCE { version INTEGER, privateKey OCTET STRING, [0] OID, [1] publicKey } 79 + # We'll use a simpler approach: just the private key with curve params 80 + 81 + # EC PARAMETERS for secp256k1 82 + ec_params = bytes([ 83 + 0x30, 0x07, # SEQUENCE, 7 bytes 84 + 0x06, 0x05, 0x2b, 0x81, 0x04, 0x00, 0x0a # OID for secp256k1 85 + ]) 86 + 87 + # EC PRIVATE KEY structure 88 + # SEQUENCE { version, privateKey, [0] parameters } 89 + inner = bytes([0x02, 0x01, 0x01]) # version = 1 90 + inner += bytes([0x04, 0x20]) + priv_bytes # OCTET STRING with 32-byte key 91 + inner += bytes([0xa0, 0x07]) + bytes([0x06, 0x05, 0x2b, 0x81, 0x04, 0x00, 0x0a]) # [0] OID 92 + 93 + # Wrap in SEQUENCE 94 + key_der = bytes([0x30, len(inner)]) + inner 95 + 96 + # Base64 encode 97 + import base64 98 + key_b64 = base64.b64encode(key_der).decode('ascii') 99 + 100 + # Format as PEM 101 + print("-----BEGIN EC PRIVATE KEY-----") 102 + for i in range(0, len(key_b64), 64): 103 + print(key_b64[i:i+64]) 104 + print("-----END EC PRIVATE KEY-----") 105 + EOF 106 + 107 + # Extract the compressed public key 108 + PUBLIC_KEY_HEX=$(openssl ec -in "$TEMP_DIR/private.pem" -pubout -conv_form compressed -outform DER 2>/dev/null | \ 109 + tail -c 33 | xxd -p | tr -d '\n') 110 + 111 + # Clean up 112 + rm -rf "$TEMP_DIR" 113 + 114 + if [ -z "$PUBLIC_KEY_HEX" ] || [ ${#PUBLIC_KEY_HEX} -ne 66 ]; then 115 + error "Failed to derive public key. Got: $PUBLIC_KEY_HEX" 116 + fi 117 + 118 + log "Derived public key: ${PUBLIC_KEY_HEX:0:8}...${PUBLIC_KEY_HEX: -8}" 119 + 120 + # Encode public key as multibase with multicodec 121 + PUBLIC_KEY_MULTIBASE=$(python3 << EOF 122 + import base58 123 + 124 + # Compressed public key bytes 125 + pub_hex = "$PUBLIC_KEY_HEX" 126 + pub_bytes = bytes.fromhex(pub_hex) 127 + 128 + # Prepend multicodec 0xe7 for secp256k1-pub 129 + # 0xe7 as varint is just 0xe7 (single byte, < 128) 130 + multicodec = bytes([0xe7, 0x01]) # 0xe701 for secp256k1-pub compressed 131 + key_with_codec = multicodec + pub_bytes 132 + 133 + # Base58btc encode 134 + encoded = base58.b58encode(key_with_codec).decode('ascii') 135 + 136 + # Add 'z' prefix for multibase 137 + print('z' + encoded) 138 + EOF 139 + ) 140 + 141 + log "Public key multibase: $PUBLIC_KEY_MULTIBASE" 142 + 143 + # Generate the did.json file 144 + log "Generating did.json..." 145 + 146 + mkdir -p "$OUTPUT_DIR" 147 + 148 + cat > "$OUTPUT_DIR/did.json" << EOF 149 + { 150 + "id": "did:web:coves.social", 151 + "alsoKnownAs": ["at://coves.social"], 152 + "verificationMethod": [ 153 + { 154 + "id": "did:web:coves.social#atproto", 155 + "type": "Multikey", 156 + "controller": "did:web:coves.social", 157 + "publicKeyMultibase": "$PUBLIC_KEY_MULTIBASE" 158 + } 159 + ], 160 + "service": [ 161 + { 162 + "id": "#atproto_pds", 163 + "type": "AtprotoPersonalDataServer", 164 + "serviceEndpoint": "https://coves.me" 165 + } 166 + ] 167 + } 168 + EOF 169 + 170 + log "Created: $OUTPUT_DIR/did.json" 171 + echo "" 172 + echo "============================================" 173 + echo " DID Document Generated Successfully!" 174 + echo "============================================" 175 + echo "" 176 + echo "Public key multibase: $PUBLIC_KEY_MULTIBASE" 177 + echo "" 178 + echo "Next steps:" 179 + echo " 1. Copy this file to your production server:" 180 + echo " scp $OUTPUT_DIR/did.json user@server:/opt/coves/static/.well-known/" 181 + echo "" 182 + echo " 2. Or if running on production, restart Caddy:" 183 + echo " docker compose -f docker-compose.prod.yml restart caddy" 184 + echo "" 185 + echo " 3. Verify it's accessible:" 186 + echo " curl https://coves.social/.well-known/did.json" 187 + echo ""