Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
63 changes: 10 additions & 53 deletions cryptography/utils.sh → cryptography/cryptography.sh
Original file line number Diff line number Diff line change
Expand Up @@ -98,63 +98,20 @@ ecdsa_verify() {
fi
}

# This function generates an ID signature based on the provided challenge data,
# ephemeral public key, destination node ID, and private key.
id_sign() {
local challenge_data="$1"
local ephemeral_public_key="$2"
local dest_node_id="$3"
local private_key="$4"

# Compute SHA256 hash of the concatenated binary data
local id_signature_text="discovery v5 identity proof"
local id_signature_hash=$( (printf "%s" "$id_signature_text"; printf "%s%s%s" "$challenge_data" "$ephemeral_public_key" "$dest_node_id" | hex_to_bin) | sha256sum | awk '{print $1}')

# Use the Python script to generate the signature
local signature=$(ecdsa_sign "$id_signature_hash" "$private_key")
if [ $? -ne 0 ] || [ -z "$signature" ]; then
printf "Error: Failed to generate signature\n" >&2
return 1
fi

# Ensure the signature is the correct length (64 bytes in hex)
if [ ${#signature} -ne 128 ]; then
printf "Error: Signature has incorrect length: expected 128, got %d\n" "${#signature}" >&2
return 1
fi

# Return the signature
printf "%s" "$signature"
}

id_verify() {
local challenge_data="$1"
local ephemeral_public_key="$2"
local dest_node_id="$3"
local signature="$4"
local static_public_key="$5"
# This function computes the Keccak-256 hash of the input.
# Note: Inputs are expected to be hex encoded
keccak_256() {
local input

# Compute SHA256 hash of the concatenated binary data
local id_signature_text="discovery v5 identity proof"
local id_signature_hash=$( (printf "%s" "$id_signature_text"; printf "%s%s%s" "$challenge_data" "$ephemeral_public_key" "$dest_node_id" | hex_to_bin) | sha256sum | awk '{print $1}')

# Verify the signature
if ecdsa_verify "$id_signature_hash" "$signature" "$static_public_key"; then
return 0
else
return 1
fi
}

# This function generates a SHA256 hash of the input
sha256() {
# if file descriptor 0 (stdin) is associated with a terminal
if [ -t 0 ]; then
openssl dgst -sha256 -binary "$1"

# Use command-line argument
input="$1"

# otherwise, the input is being piped from another command or redirected from a file
else
# If input is piped, read from stdin
cat - | openssl dgst -sha256 -binary
# Read from pipe
input=$(cat)
fi
python cryptography/keccak_256.py "$input"
}
45 changes: 45 additions & 0 deletions cryptography/derive_public_key.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
#!/usr/bin/env python3

import sys
import binascii
from eth_keys import keys

def derive_public_key(private_key_hex):
try:
# Convert hex input to bytes
private_key_bytes = binascii.unhexlify(private_key_hex)

# Create a PrivateKey object
private_key = keys.PrivateKey(private_key_bytes)

# Get the public key
public_key = private_key.public_key

# Get the uncompressed public key
uncompressed_public_key = public_key.to_bytes()

# Get the compressed public key
compressed_public_key = public_key.to_compressed_bytes()

return {
"uncompressed": uncompressed_public_key.hex(),
"compressed": compressed_public_key.hex()
}
except binascii.Error:
return "Error: Invalid hexadecimal in private key"
except Exception as e:
return f"Error: {str(e)}"

if __name__ == "__main__":
if len(sys.argv) != 2:
print("Usage: python derive_public_key.py <private_key_hex>")
sys.exit(1)

private_key_hex = sys.argv[1]
result = derive_public_key(private_key_hex)

if isinstance(result, dict):
print(f"Uncompressed Public Key: {result['uncompressed']}")
print(f"Compressed Public Key: {result['compressed']}")
else:
print(result)
5 changes: 0 additions & 5 deletions cryptography/ecdsa_sign.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,6 @@ def ecdsa_sign(message_hash_hex, private_key_hex):

# Get the signature as r || s (64 bytes)
signature_bytes = signature.r.to_bytes(32, 'big') + signature.s.to_bytes(32, 'big')

# Verify the signature immediately
public_key = private_key.public_key
is_valid = public_key.verify_msg_hash(message_hash_bytes, signature)

return signature_bytes.hex()

if __name__ == '__main__':
Expand Down
30 changes: 30 additions & 0 deletions cryptography/keccak_256.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import sys
import binascii
from Crypto.Hash import keccak

def keccak_hash(data):
data = binascii.unhexlify(data)

# Create a new Keccak hash object
k = keccak.new(digest_bits=256)

# Update the hash object with the data
k.update(data)

# Return the hexadecimal representation of the hash
return k.hexdigest()

if __name__ == "__main__":
if len(sys.argv) < 1:
print("Usage: keccak_hash.py <input_data> [hash_bits]")
print(" <input_data>: The data to hash (string or hex)")
sys.exit(1)

input_data = sys.argv[1]

try:
result = keccak_hash(input_data)
print(result)
except Exception as e:
print(f"Error: {str(e)}")
sys.exit(1)
50 changes: 1 addition & 49 deletions cryptography/tests/run_tests.sh
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )"
source $DIR/../../discv5/utils.sh
source $DIR/../utils.sh
source $DIR/../cryptography.sh

test_generate_secp256k1_keypair() {
# Call the function
Expand Down Expand Up @@ -54,52 +54,4 @@ test_generate_secp256k1_keypair() {
fi
}

test_id_sign(){
static_private_key="fb757dc581730490a1d7a00deea65e9b1936924caaea8f44d476014856b68736"
challenge_data="000000000000000000000000000000006469736376350001010102030405060708090a0b0c00180102030405060708090a0b0c0d0e0f100000000000000000"
ephemeral_public_key="039961e4c2356d61bedb83052c115d311acb3a96f5777296dcf297351130266231"
dest_node_id="bbbb9d047f0488c0b5a93c1c3f2d8bafc7c8ff337024a55434a0d0555de64db9"

local calculated_id_signature=$(id_sign "$challenge_data" "$ephemeral_public_key" "$dest_node_id" "$static_private_key")
local expected_id_signature="94852a1e2318c4e5e9d422c98eaf19d1d90d876b29cd06ca7cb7546d0fff7b484fe86c09a064fe72bdbef73ba8e9c34df0cd2b53e9d65528c2c7f336d5dfc6e6"

if [[ "$calculated_id_signature" != "$expected_id_signature" ]]; then
printf "test_id_sign: Test FAILED: signature mismatch.\n"
printf "Calculated: %s\n" "$calculated_id_signature"
printf "Expected: %s\n" "$expected_id_signature"
else
printf "test_id_sign: Test PASSED: signature matches.\n"
fi
}

test_id_sign_and_verify() {
# Test inputs
static_private_key="fb757dc581730490a1d7a00deea65e9b1936924caaea8f44d476014856b68736"
static_public_key="030e2cb74241c0c4fc8e8166f1a79a05d5b0dd95813a74b094529f317d5c39d235"
challenge_data="000000000000000000000000000000006469736376350001010102030405060708090a0b0c00180102030405060708090a0b0c0d0e0f100000000000000000"
ephemeral_pubkey="039961e4c2356d61bedb83052c115d311acb3a96f5777296dcf297351130266231"
dest_node_id="bbbb9d047f0488c0b5a93c1c3f2d8bafc7c8ff337024a55434a0d0555de64db9"

# Generate the signature using id_sign
local calculated_id_signature
calculated_id_signature=$(id_sign "$challenge_data" "$ephemeral_pubkey" "$dest_node_id" "$static_private_key")
if [ $? -ne 0 ]; then
printf "test_id_sign_and_verify: Test FAILED: id_sign function returned an error.\n"
return 1
fi

# Verify the signature using id_verify, passing static_public_key
if id_verify "$challenge_data" "$ephemeral_pubkey" "$dest_node_id" "$calculated_id_signature" "$static_public_key"; then
printf "test_id_sign_and_verify: Test PASSED: Signature verified successfully.\n"
return 0
else
printf "test_id_sign_and_verify: Test FAILED: Signature verification failed.\n"
return 1
fi
}

test_generate_secp256k1_keypair
printf "\n"
test_id_sign
printf "\n"
test_id_sign_and_verify
2 changes: 1 addition & 1 deletion discv5/discv5_codec.sh → discv5/codec.sh
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )"
source $DIR/utils.sh
source $DIR/../rlp/rlp_codec.sh
source $DIR/../rlp/rlp.sh

# Function to encode header
encrypt_masked_header() {
Expand Down
2 changes: 1 addition & 1 deletion discv5/discv5.sh
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
#!/bin/bash

DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )"
source $DIR/discv5_codec.sh
source $DIR/codec.sh
source $DIR/../discv5/udp_duplex.sh

# Set up the trap for Ctrl+C (SIGINT)
Expand Down
54 changes: 51 additions & 3 deletions discv5/tests/run_tests.sh
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
#!/bin/bash

DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )"
source $DIR/../../cryptography/utils.sh
source $DIR/../discv5_codec.sh
source $DIR/../../cryptography/cryptography.sh
source $DIR/../codec.sh

# Test encoding and decoding of PING message
test_ping_message() {
Expand Down Expand Up @@ -297,6 +297,50 @@ test_handshake_message() {
fi
}

test_id_sign(){
static_private_key="fb757dc581730490a1d7a00deea65e9b1936924caaea8f44d476014856b68736"
challenge_data="000000000000000000000000000000006469736376350001010102030405060708090a0b0c00180102030405060708090a0b0c0d0e0f100000000000000000"
ephemeral_public_key="039961e4c2356d61bedb83052c115d311acb3a96f5777296dcf297351130266231"
dest_node_id="bbbb9d047f0488c0b5a93c1c3f2d8bafc7c8ff337024a55434a0d0555de64db9"

local calculated_id_signature=$(id_sign "$challenge_data" "$ephemeral_public_key" "$dest_node_id" "$static_private_key")
local expected_id_signature="94852a1e2318c4e5e9d422c98eaf19d1d90d876b29cd06ca7cb7546d0fff7b484fe86c09a064fe72bdbef73ba8e9c34df0cd2b53e9d65528c2c7f336d5dfc6e6"

if [[ "$calculated_id_signature" != "$expected_id_signature" ]]; then
printf "test_id_sign: Test FAILED: signature mismatch.\n"
printf "Calculated: %s\n" "$calculated_id_signature"
printf "Expected: %s\n" "$expected_id_signature"
else
printf "test_id_sign.\n Test PASSED: signature matches.\n"
fi
}

test_id_sign_and_verify() {
# Test inputs
static_private_key="fb757dc581730490a1d7a00deea65e9b1936924caaea8f44d476014856b68736"
static_public_key="030e2cb74241c0c4fc8e8166f1a79a05d5b0dd95813a74b094529f317d5c39d235"
challenge_data="000000000000000000000000000000006469736376350001010102030405060708090a0b0c00180102030405060708090a0b0c0d0e0f100000000000000000"
ephemeral_pubkey="039961e4c2356d61bedb83052c115d311acb3a96f5777296dcf297351130266231"
dest_node_id="bbbb9d047f0488c0b5a93c1c3f2d8bafc7c8ff337024a55434a0d0555de64db9"

# Generate the signature using id_sign
local calculated_id_signature
calculated_id_signature=$(id_sign "$challenge_data" "$ephemeral_pubkey" "$dest_node_id" "$static_private_key")
if [ $? -ne 0 ]; then
printf "test_id_sign_and_verify.\n Test FAILED: id_sign function returned an error.\n"
return 1
fi

# Verify the signature using id_verify, passing static_public_key
if id_verify "$challenge_data" "$ephemeral_pubkey" "$dest_node_id" "$calculated_id_signature" "$static_public_key"; then
printf "test_id_sign_and_verify.\n Test PASSED: Signature verified successfully.\n"
return 0
else
printf "test_id_sign_and_verify.\n Test FAILED: Signature verification failed.\n"
return 1
fi
}

test_ping_message
printf "\n"
test_pong_message
Expand All @@ -307,4 +351,8 @@ test_nodes_message
printf "\n"
test_whoareyou_message
printf "\n"
test_handshake_message
test_handshake_message
printf "\n"
test_id_sign
printf "\n"
test_id_sign_and_verify
2 changes: 1 addition & 1 deletion discv5/tests/run_tests_from_file.sh
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
#!/bin/bash

DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )"
source $DIR/../discv5_codec.sh
source $DIR/../codec.sh

# Function to parse JSON
parse_json() {
Expand Down
4 changes: 2 additions & 2 deletions discv5/udp_duplex.sh
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
#!/bin/bash

DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )"
source $DIR/../cryptography/utils.sh
source $DIR/discv5_codec.sh
source $DIR/../cryptography/cryptography.sh
source $DIR/codec.sh

# Create temporary files for our queues
INCOMING_QUEUE=$(mktemp /tmp/discv5_incoming.XXXXXX)
Expand Down
48 changes: 48 additions & 0 deletions discv5/utils.sh
Original file line number Diff line number Diff line change
Expand Up @@ -32,3 +32,51 @@ generate_random_bytes() {
done
printf "%s" "$result"
}

# This function generates an ID signature based on the provided challenge data,
# ephemeral public key, destination node ID, and private key.
id_sign() {
local challenge_data="$1"
local ephemeral_public_key="$2"
local dest_node_id="$3"
local private_key="$4"

# Compute SHA256 hash of the concatenated binary data
local id_signature_text="discovery v5 identity proof"
local id_signature_hash=$( (printf "%s" "$id_signature_text"; printf "%s%s%s" "$challenge_data" "$ephemeral_public_key" "$dest_node_id" | hex_to_bin) | sha256sum | cut -d' ' -f1)

# Use the Python script to generate the signature
local signature=$(ecdsa_sign "$id_signature_hash" "$private_key")
if [ $? -ne 0 ] || [ -z "$signature" ]; then
printf "Error: Failed to generate signature\n" >&2
return 1
fi

# Ensure the signature is the correct length (64 bytes in hex)
if [ ${#signature} -ne 128 ]; then
printf "Error: Signature has incorrect length: expected 128, got %d\n" "${#signature}" >&2
return 1
fi

# Return the signature
printf "%s" "$signature"
}

id_verify() {
local challenge_data="$1"
local ephemeral_public_key="$2"
local dest_node_id="$3"
local signature="$4"
local static_public_key="$5"

# Compute SHA256 hash of the concatenated binary data
local id_signature_text="discovery v5 identity proof"
local id_signature_hash=$( (printf "%s" "$id_signature_text"; printf "%s%s%s" "$challenge_data" "$ephemeral_public_key" "$dest_node_id" | hex_to_bin) | sha256sum | cut -d' ' -f1)

# Verify the signature
if ecdsa_verify "$id_signature_hash" "$signature" "$static_public_key"; then
return 0
else
return 1
fi
}
Loading