-
Notifications
You must be signed in to change notification settings - Fork 375
/
proof.sh
executable file
·228 lines (185 loc) · 8.53 KB
/
proof.sh
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
#!/usr/bin/env bash
set -o errexit -o nounset -o pipefail
# TODO: what about openssl versions? maybe use python for signing?
# TODO: tell user how to install utilities
# This script does the following:
# 1. Ask user for her GitHub username and Ethereum address (eth_addr)
# 2. Negotiate with user which SSH key to use
# 3. Find at least one match of key and encrypted_key that decrypts succesfully
# 4. Decrypt encrypted_key to tmp_eth_key
# 5. Sign sender ethereum address: sign[tmp_eth_key](eth_addr)
# 6. Encode signature (#5) and merkle proof and output result
# keys.bin format (CSV):
# GH UserName,Encrypted[userId,tmp_eth_addr,tmp_eth_key,merkle proof]
trap 'echo GOT IT ; exit 0' SIGTERM
# check_program_in_path "program"
check_program_in_path() {
program="${1}"
if ! type -p "${program}" &>/dev/null; then
printf '%s\n' "error: ${program} is not installed."
printf '%s\n' "You should run install script first"
printf '%s\n' "or use your package manager to install it."
exit 1
fi
}
# while true; do :; done
# check that everything installed
PATH="./bin:${PATH}"
for i in age base64 sha3sum; do
check_program_in_path $i
done
SSH_KEYS_DIR="$HOME/.ssh"
ask_ssh_key() {
SSH_KEYS=()
# list all files from ~/.ssh, except for *.pub, known_hosts, config and log files (tmux sometimes puts logs there)
while IFS= read -r -d $'\0'; do
SSH_KEYS+=("$REPLY")
done < <(find "$SSH_KEYS_DIR" -mindepth 1 -maxdepth 1 ! -name "*.pub" ! -name "known_hosts*" ! -name "config" ! -name "*.log" -print0)
select fname in "${SSH_KEYS[@]}"; do
echo "$fname"
break
done
}
WORK_DIR="$(pwd)/__sh_cache__"
DECRYPTED_DATA="$WORK_DIR/decrypted.data"
ETH_KEY_DER="$WORK_DIR/tmp_eth.key.der"
ETH_KEY="$WORK_DIR/tmp_eth.key"
OPENSSL_STDERR="$WORK_DIR/openssl.stderr"
AGE_STDERR="$WORK_DIR/age.stderr"
mkdir -p $WORK_DIR
METADATA_BIN="metadata.bin"
# $# is the number of arguments
if [ $# -gt 1 ]; then
GITHUB_USERNAME="$1"
ETHEREUM_ADDRESS="$2"
else
if [ ! -f "$METADATA_BIN" ]; then
echo "$METADATA_BIN doesn't exist"
exit 1
fi
printf "\nWelcome to the proof generation script for Fluence Developer Reward Airdrop."
printf "\n5%% of the FLT supply is allocated to ~110,000 developers who contributed into open source web3 repositories during last year."
printf "\nPublic keys of selected Github accounts were added into a smart contract on Ethereum. Claim your allocation and help us build the decentralized internet together!"
printf "\n"
printf "\nCheck if you are eligible and proceed with claiming"
read -r -p "Enter your github username so we can check if you are participating in the airdrop: " GITHUB_USERNAME
printf "\nEthereum wallet address is necessary to generate a proof that you will send through our web page."
printf "\n\033[33mImportant notice: you need to make a claim transaction from the entered address!\033[0m\n\n"
read -r -p "Enter the ethereum address to which you plan to receive the airdrop: " ETHEREUM_ADDRESS
STR_LENGTH=$(echo "$ETHEREUM_ADDRESS" | sed -e 's/^0x//' | awk '{ print length }')
if [ "$STR_LENGTH" -ne 40 ]; then
echo "$ETHEREUM_ADDRESS is not an Ethereum address. Must be of 40 or 42 (with 0x) characters, was $STR_LENGTH chars"
exit 1
fi
NON_HEX_BYTES_LENGTH=$(echo "$ETHEREUM_ADDRESS" | sed -e 's/^0x//' | tr -d "[:xdigit:]" | awk '{ print length }')
if [ "$NON_HEX_BYTES_LENGTH" -ne 0 ]; then
echo "$ETHEREUM_ADDRESS is not an Ethereum address. Must be hexadecimal, has non-hexadecimal symbols."
exit 1
fi
fi
KEY_ARG_PATH=''
if [ $# -gt 2 ]; then
KEY_ARG_PATH="$3"
fi
ENCRYPTED_KEYS=()
while IFS='' read -r line; do ENCRYPTED_KEYS+=("$line"); done < <(grep -i "^${GITHUB_USERNAME}," "${METADATA_BIN}" || true)
# ${#ENCRYPTED_KEYS[@]} -- calculates number of elements in the array
if [ ${#ENCRYPTED_KEYS[@]} -gt 1 ]; then
echo "Found ${#ENCRYPTED_KEYS[@]} encrypted keys for your GitHub username. That means you have several SSH keys published on GitHub"
# echo "Any of your keys would work. We have encrypted a temporary keypair for each of your SSH keys."
elif [ ${#ENCRYPTED_KEYS[@]} -gt 0 ]; then
echo "Found an encrypted key for your GitHub username"
else
echo "This'$GITHUB_USERNAME' Github account is not eligible for claiming"
exit 1
fi
printf "\n\tNOTE: your SSH key is used ONLY LOCALLY to decrypt a message and generate Token Claim Proof."
printf "\n\tScript will explicitly ask your consent before using the key."
printf "\n\tIf you have any technical issues, take a look at the following logs:\n\t\t$OPENSSL_STDERR\n\t\t$AGE_STDERR\n\tReport any issues to https://fluence.chat \n\n"
printf "Now the script needs your ssh key to generate proof. \n"
while true; do
if [ -n "$KEY_ARG_PATH" ] && [ -f "$KEY_ARG_PATH" ]; then
KEY_PATH=$KEY_ARG_PATH
else
if [ -d "$SSH_KEYS_DIR" ]; then
# shellcheck disable=SC2162 # user can have spaces in the path to ssh key and use backslashes to escape them
read -p "Enter path to the private SSH key to use or just press Enter to show existing keys: " KEY_PATH
if [ -z "$KEY_PATH" ]; then
KEY_PATH=$(ask_ssh_key)
fi
else
# shellcheck disable=SC2162 # user can have spaces in the path to ssh key and use backslashes to escape them
read -p "Enter path to the private SSH key to use: " KEY_PATH
if [ -z "$KEY_PATH" ]; then
continue
fi
fi
if ! [ -f "$KEY_PATH" ]; then
echo "Specified $KEY_PATH file does not exits or not a SSH private key"
continue
fi
read -p "Will use SSH key to generate proof data. Press enter to proceed. "
printf "\n"
fi
rm -f "$DECRYPTED_DATA"
printf "\n"
for encrypted in "${ENCRYPTED_KEYS[@]}"; do
# contains encrypted (user_id, eth_tmp_key, merkle proof)
ENCRYPTED_DATA=$(echo "$encrypted" | cut -d',' -f2)
set +o errexit
echo "$ENCRYPTED_DATA" | xxd -r -p -c 1000 | age --decrypt --identity "$KEY_PATH" --output "$DECRYPTED_DATA" 2>$AGE_STDERR
exit_code=$?
set -o errexit
if [ $exit_code -ne 0 ]; then
continue
else
break
fi
done
if [ -e "$DECRYPTED_DATA" ]; then
# echo "Decrypted succesfully! Decrypted data is at $DECRYPTED_DATA"
break
else
echo "Couldn't decrypt with that SSH key, please choose another one."
echo "Possible causes are:"
echo "You have specified the file which doesn't contain valid private key."
echo "Your private key doesn't match your public key in GitHub. It could happen if you've changed local ssh key recently."
echo "Internal error:"
# replace report URL in $AGE_STDERR
STDERR_TMP="$(mktemp)"
cat "$AGE_STDERR" | sed -e 's#https://filippo.io/age/report#https://fluence.chat#g' > "$STDERR_TMP"
cat "$STDERR_TMP" > "$AGE_STDERR"
# print Age error with replaced report URL
cat "$AGE_STDERR"
fi
done
## Prepare real ethereum address to be hashed and signed
ETH_ADDR_HEX_ONLY=$(echo -n "$ETHEREUM_ADDRESS" | sed -e 's/^0x//')
# length of ETH key is always 20 bytes
LENGTH="20"
PREFIX_HEX=$(echo -n $'\x19Ethereum Signed Message:\n'${LENGTH} | xxd -p)
DATA_HEX="${PREFIX_HEX}${ETH_ADDR_HEX_ONLY}"
## '|| true' is needed to work around this bug https://gitlab.com/kurdy/sha3sum/-/issues/2
HASH=$(echo -n "$DATA_HEX" | xxd -r -p | (sha3sum -a Keccak256 -t || true) | sed 's/[^[:xdigit:]].*//')
## Write temporary eth key to file in binary format (DER)
cat "$DECRYPTED_DATA" | cut -d',' -f3 | xxd -r -p -c 118 >"$ETH_KEY_DER"
## Convert secp256k1 key from DER (binary) to textual representation
set +o errexit
openssl ec -inform der -in "$ETH_KEY_DER" 2>$OPENSSL_STDERR >"$ETH_KEY"
exit_code=$?
set -o errexit
if [ $exit_code -ne 0 ]; then
echo "Failed to parse $ETH_KEY_DER with OpenSSL. Errors below may be relevant."
echo "==="
cat $OPENSSL_STDERR
echo "==="
exit 1
fi
## Sign hash of the real ethereum address with the temporary one
SIGNATURE_HEX=$(echo "$HASH" | xxd -r -p | openssl pkeyutl -sign -inkey "$ETH_KEY" | xxd -p -c 72)
USER_ID=$(cat "$DECRYPTED_DATA" | cut -d',' -f1)
TMP_ETH_ADDR=$(cat "$DECRYPTED_DATA" | cut -d',' -f2)
MERKLE_PROOF=$(cat "$DECRYPTED_DATA" | cut -d',' -f4)
echo -e "Success! Copy the line below and paste it in the browser.\n"
# userId, tmpEthAddr, signatureHex, merkleProofHex
echo "${USER_ID},${TMP_ETH_ADDR},${SIGNATURE_HEX},${MERKLE_PROOF}"