Skip to content

Commit

Permalink
Gateway - handle Ten Gateway key exchange (#2156)
Browse files Browse the repository at this point in the history
  • Loading branch information
zkokelj authored Nov 27, 2024
1 parent 2a0b219 commit 3b59d78
Show file tree
Hide file tree
Showing 16 changed files with 1,526 additions and 20 deletions.
29 changes: 28 additions & 1 deletion .github/workflows/manual-deploy-obscuro-gateway.yml
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,7 @@ jobs:
"GATEWAY_RATE_LIMIT_USER_COMPUTE_TIME"
"GATEWAY_RATE_LIMIT_WINDOW"
"GATEWAY_MAX_CONCURRENT_REQUESTS_PER_USER"
"GATEWAY_KEY_EXCHANGE_URL"
)
for VAR_NAME in "${VAR_NAMES[@]}"; do
Expand Down Expand Up @@ -119,6 +120,7 @@ jobs:
echo "GATEWAY_RATE_LIMIT_USER_COMPUTE_TIME: $GATEWAY_RATE_LIMIT_USER_COMPUTE_TIME"
echo "GATEWAY_RATE_LIMIT_WINDOW: $GATEWAY_RATE_LIMIT_WINDOW"
echo "GATEWAY_MAX_CONCURRENT_REQUESTS_PER_USER: $GATEWAY_MAX_CONCURRENT_REQUESTS_PER_USER"
echo "GATEWAY_KEY_EXCHANGE_URL: $GATEWAY_KEY_EXCHANGE_URL"
- name: "Print GitHub variables"
# This is a useful record of what the environment variables were at the time the job ran, for debugging and reference
Expand Down Expand Up @@ -351,13 +353,38 @@ jobs:
gcr.io/cadvisor/cadvisor:latest
# Promtail Integration End
# Create a named volume for persistence
docker volume create "${{ env.VM_NAME }}-data"
# Start Ten Gateway Container
docker run -d -p 80:80 -p 81:81 --name "${{ env.VM_NAME }}" \
--device /dev/sgx_enclave --device /dev/sgx_provision \
-v "${{ env.VM_NAME }}-data:/data" \
-e OBSCURO_GATEWAY_VERSION="${{ github.run_number }}-${{ github.sha }}" \
-e OE_SIMULATION=0 \
"${{ env.DOCKER_BUILD_TAG_GATEWAY }}" \
ego run /home/ten/go-ten/tools/walletextension/main/main \
-host=0.0.0.0 -port=80 -portWS=81 -nodeHost="${{ env.L2_RPC_URL_VALIDATOR }}" -verbose=true \
-logPath=sys_out -dbType=cosmosDB -dbConnectionURL="${{ secrets.COSMOS_DB_CONNECTION_STRING }}" \
-rateLimitUserComputeTime="${{ env.GATEWAY_RATE_LIMIT_USER_COMPUTE_TIME }}" -rateLimitWindow="${{ env.GATEWAY_RATE_LIMIT_WINDOW }}" -maxConcurrentRequestsPerUser="${{ env.GATEWAY_MAX_CONCURRENT_REQUESTS_PER_USER }}" '
-rateLimitUserComputeTime="${{ env.GATEWAY_RATE_LIMIT_USER_COMPUTE_TIME }}" \
-rateLimitWindow="${{ env.GATEWAY_RATE_LIMIT_WINDOW }}" \
-maxConcurrentRequestsPerUser="${{ env.GATEWAY_MAX_CONCURRENT_REQUESTS_PER_USER }}"
-keyExchangeURL="${{ env.GATEWAY_KEY_EXCHANGE_URL }}" \
-insideEnclave=true \
# After starting the container, verify the volume mount
docker exec "${{ env.VM_NAME }}" sh -c "
echo \"Checking volume mount...\";
df -h | grep /data;
echo \"Directory listing:\";
ls -la /data;
echo \"Current working directory:\";
pwd;
echo \"Directory permissions:\";
ls -la /;
echo \"Process status:\";
ps aux;
"
'
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,7 @@ require (
github.com/docker/distribution v2.8.3+incompatible // indirect
github.com/docker/go-units v0.5.0 // indirect
github.com/dustin/go-humanize v1.0.1 // indirect
github.com/ecies/go/v2 v2.0.9 // indirect
github.com/ethereum/c-kzg-4844 v1.0.3 // indirect
github.com/ethereum/go-verkle v0.1.1-0.20240306133620-7d920df305f0 // indirect
github.com/fsnotify/fsnotify v1.7.0 // indirect
Expand Down
1,017 changes: 1,017 additions & 0 deletions go.sum

Large diffs are not rendered by default.

3 changes: 2 additions & 1 deletion tools/walletextension/common/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,5 +19,6 @@ type Config struct {
RateLimitUserComputeTime time.Duration
RateLimitWindow time.Duration
RateLimitMaxConcurrentRequests int
Debug bool
InsideEnclave bool // Indicates if the program is running inside an enclave
KeyExchangeURL string
}
1 change: 1 addition & 0 deletions tools/walletextension/common/constants.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ const (
PathSessionKeys = "/session-key/"
PathNetworkHealth = "/network-health/"
PathNetworkConfig = "/network-config/"
PathKeyExchange = "/key-exchange/"
WSProtocol = "ws://"
HTTPProtocol = "http://"
EncryptedTokenQueryParameter = "token"
Expand Down
23 changes: 17 additions & 6 deletions tools/walletextension/enclave.Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -6,17 +6,17 @@

# Final container folder structure:
# /home/ten/go-ten/tools/walletextension/main contains the executable for the enclave
# /data persistent volume mount point


# Trigger new build stage for compiling the enclave
FROM ghcr.io/edgelesssys/ego-dev:v1.5.3 AS build-base

# Install ca-certificates package and update it
RUN apt-get update && apt-get install -y \
ca-certificates \
&& update-ca-certificates


# setup container data structure
# Setup container data structure
RUN mkdir -p /home/ten/go-ten

# Ensures container layer caching when dependencies are not changed
Expand All @@ -39,16 +39,27 @@ RUN --mount=type=cache,target=/root/.cache/go-build \
# Sign the enclave executable
RUN ego sign enclave.json


# Trigger a new build stage and use the smaller ego version:
FROM ghcr.io/edgelesssys/ego-deploy:v1.5.3

# Create data directory that will be used for persistence
RUN mkdir -p /data && chmod 777 /data

# Copy just the binary for the enclave into this build stage
COPY --from=build-enclave \
/home/ten/go-ten/tools/walletextension/main /home/ten/go-ten/tools/walletextension/main

# Copy the entry.sh script and make it executable
COPY tools/walletextension/main/entry.sh /home/ten/go-ten/tools/walletextension/main/entry.sh
RUN chmod +x /home/ten/go-ten/tools/walletextension/main/entry.sh

WORKDIR /home/ten/go-ten/tools/walletextension/main

# Add volume mount point
VOLUME ["/data"]

# simulation mode is ACTIVE by default
ENV OE_SIMULATION=1
EXPOSE 3000
EXPOSE 3000

# Set the entrypoint to entry.sh
ENTRYPOINT ["/home/ten/go-ten/tools/walletextension/main/entry.sh"]
5 changes: 5 additions & 0 deletions tools/walletextension/encryption/encryption.go
Original file line number Diff line number Diff line change
Expand Up @@ -63,3 +63,8 @@ func (e *Encryptor) HashWithHMAC(data []byte) []byte {
h.Write(data)
return h.Sum(nil)
}

// GetKey returns the encryption key
func (e *Encryptor) GetKey() []byte {
return e.key
}
89 changes: 89 additions & 0 deletions tools/walletextension/httpapi/routes.go
Original file line number Diff line number Diff line change
@@ -1,11 +1,15 @@
package httpapi

import (
"bytes"
"crypto/sha256"
"encoding/hex"
"encoding/json"
"fmt"
"net/http"

tencommon "github.com/ten-protocol/go-ten/go/common"
"github.com/ten-protocol/go-ten/tools/walletextension/keymanager"
"github.com/ten-protocol/go-ten/tools/walletextension/services"

"github.com/status-im/keycard-go/hexutils"
Expand Down Expand Up @@ -62,6 +66,10 @@ func NewHTTPRoutes(walletExt *services.Services) []node.Route {
Name: common.APIVersion1 + common.PathNetworkConfig,
Func: httpHandler(walletExt, networkConfigRequestHandler),
},
{
Name: common.APIVersion1 + common.PathKeyExchange,
Func: httpHandler(walletExt, keyExchangeRequestHandler),
},
{
Name: common.APIVersion1 + common.PathSessionKeys + "create",
Func: httpHandler(walletExt, createSKRequestHandler),
Expand Down Expand Up @@ -613,3 +621,84 @@ func boolToByte(res bool) byte {
}
return 0
}

func keyExchangeRequestHandler(walletExt *services.Services, conn UserConn) {
// Read the request
body, err := conn.ReadRequest()
if err != nil {
handleError(conn, walletExt.Logger(), fmt.Errorf("error reading request: %w", err))
return
}

// Step 1: Deserialize the received message
var receivedMessageOG keymanager.KeyExchangeRequest
err = json.Unmarshal(body, &receivedMessageOG)
if err != nil {
walletExt.Logger().Error("OG: Failed to deserialize received message", log.ErrKey, err)
handleError(conn, walletExt.Logger(), fmt.Errorf("failed to deserialize message: %w", err))
return
}

// Step 2: Deserialize the public key
receivedPubKey, err := keymanager.DeserializePublicKey(receivedMessageOG.PublicKey)
if err != nil {
walletExt.Logger().Error("OG: Failed to deserialize public key", log.ErrKey, err)
handleError(conn, walletExt.Logger(), fmt.Errorf("failed to deserialize public key: %w", err))
return
}

// Step 3: Deserialize the attestation report
var receivedAttestation tencommon.AttestationReport
if err := json.Unmarshal(receivedMessageOG.Attestation, &receivedAttestation); err != nil {
handleError(conn, walletExt.Logger(), fmt.Errorf("error unmarshaling attestation report: %w", err))
return
}

// Step 4: Verify the attestation report
verifiedData, err := keymanager.VerifyReport(&receivedAttestation)
if err != nil {
walletExt.Logger().Error("OG: Failed to verify attestation report", log.ErrKey, err)
handleError(conn, walletExt.Logger(), fmt.Errorf("failed to verify attestation report: %w", err))
return
}

// Hash the received public key bytes
pubKeyHash := sha256.Sum256(receivedMessageOG.PublicKey)

// Only compare the first 32 bytes since verifiedData is padded to 64 bytes
verifiedDataTruncated := verifiedData[:32]
if bytes.Equal(verifiedDataTruncated, pubKeyHash[:]) {
walletExt.Logger().Info("OG: Public keys match")
} else {
walletExt.Logger().Error("OG: Public keys do not match")
}

// Step 5 Encrypt the encryption key using the received public key
encryptedKeyOG, err := keymanager.EncryptWithPublicKey(walletExt.Storage.GetEncryptionKey(), receivedPubKey)
if err != nil {
walletExt.Logger().Error("OG: Encryption failed", log.ErrKey, err)
handleError(conn, walletExt.Logger(), fmt.Errorf("encryption failed: %w", err))
return
}

// Step 6: Encode the encrypted encryption key to Base64
encodedEncryptedKeyOG := keymanager.EncodeBase64(encryptedKeyOG)

// Step 7: Create the response message containing the encrypted key
messageOG := keymanager.KeyExchangeResponse{
EncryptedKey: encodedEncryptedKeyOG,
}

// Step 8: Serialize the response message to JSON and send it back to the requester
messageBytesOG, err := json.Marshal(messageOG)
if err != nil {
walletExt.Logger().Error("OG: Failed to serialize response message", log.ErrKey, err)
handleError(conn, walletExt.Logger(), fmt.Errorf("failed to serialize response message: %w", err))
return
}
walletExt.Logger().Info("Shared encrypted key with another gateway enclave")
err = conn.WriteResponse(messageBytesOG)
if err != nil {
walletExt.Logger().Error("error writing response", log.ErrKey, err)
}
}
Loading

0 comments on commit 3b59d78

Please sign in to comment.