Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: fix derive key and update remote attestation #2303

Merged
merged 2 commits into from
Jan 14, 2025
Merged
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
18 changes: 12 additions & 6 deletions docs/docs/advanced/eliza-in-tee.md
Original file line number Diff line number Diff line change
Expand Up @@ -57,16 +57,22 @@ Example usage:
const provider = new DeriveKeyProvider(teeMode);
// For Solana
const { keypair, attestation } = await provider.deriveEd25519Keypair(
"/",
secretSalt,
"solana",
agentId,
);
// For EVM
const { keypair, attestation } = await provider.deriveEcdsaKeypair(
"/",
secretSalt,
"evm",
agentId,
);

// For raw key derivation
const rawKey = await provider.deriveRawKey(
secretSalt,
"raw",
);
```

---
Expand Down Expand Up @@ -161,7 +167,7 @@ To set up your environment for TEE development:

4. **Verify TEE Attestation**

You can verify the TEE attestation quote by going to the [TEE RA Explorer](https://ra-quote-explorer.vercel.app/) and pasting the attestation quote from the agent logs. Here's an example of interacting with the Eliza agent to ask for the agent's wallet address:
You can verify the TEE attestation quote by going to the [TEE RA Explorer](https://proof.t16z.com/) and pasting the attestation quote from the agent logs. Here's an example of interacting with the Eliza agent to ask for the agent's wallet address:

```bash
You: what's your wallet address?
Expand Down Expand Up @@ -227,7 +233,7 @@ Now we are ready to deploy the Eliza agent to a real TEE environment.

### Run an Eliza Agent in a Real TEE Environment

Before deploying the Eliza agent to a real TEE environment, you need to create a new TEE account on the [TEE Cloud](https://teehouse.vercel.app). Reach out to Phala Network on [Discord](https://discord.gg/phalanetwork) if you need help.
Before deploying the Eliza agent to a real TEE environment, you need to create a new TEE account on the [TEE Cloud](https://cloud.phala.network/login). Reach out to Phala Network on [Discord](https://discord.gg/phalanetwork) if you need help.

Next, you will need to take the docker-compose.yaml file in the root folder of the project and edit it based on your agent configuration.

Expand Down Expand Up @@ -285,7 +291,7 @@ volumes:
tee:
```

Now you can deploy the Eliza agent to a real TEE environment. Go to the [TEE Cloud](https://teehouse.vercel.app) and click on the `Create VM` button to configure your Eliza agent deployment.
Now you can deploy the Eliza agent to a real TEE environment. Go to the [TEE Cloud](https://cloud.phala.network/login) and click on the `Create VM` button to configure your Eliza agent deployment.

Click on the `Compose Manifest Mode` tab and paste the docker-compose.yaml file content into the `Compose Manifest` field.

Expand Down Expand Up @@ -313,7 +319,7 @@ Click on the `Logs` tab to view the agent logs.

![Agent Logs](https://i.imgur.com/aU3i0Dv.png)

Now we can verify the REAL TEE attestation quote by going to the [TEE RA Explorer](https://ra-quote-explorer.vercel.app/) and pasting the attestation quote from the agent logs.
Now we can verify the REAL TEE attestation quote by going to the [TEE RA Explorer](https://proof.t16z.com/) and pasting the attestation quote from the agent logs.

![TEE RA Explorer](https://i.imgur.com/TJ5299l.png)

Expand Down
6 changes: 3 additions & 3 deletions docs/docs/packages/plugins.md
Original file line number Diff line number Diff line change
Expand Up @@ -469,7 +469,7 @@ const provider = new DeriveKeyProvider();
// Derive a raw key
try {
const rawKey = await provider.rawDeriveKey(
"/path/to/derive",
"/path/to/derive", // This is what the WALLET_SECRET_SALT is used for
"subject-identifier",
);
// rawKey is a DeriveKeyResponse that can be used for further processing
Expand All @@ -482,7 +482,7 @@ try {
// Derive a Solana keypair (Ed25519)
try {
const solanaKeypair = await provider.deriveEd25519Keypair(
"/path/to/derive",
"/path/to/derive", // This is what the WALLET_SECRET_SALT is used for
"subject-identifier",
);
// solanaKeypair can now be used for Solana operations
Expand All @@ -493,7 +493,7 @@ try {
// Derive an Ethereum keypair (ECDSA)
try {
const evmKeypair = await provider.deriveEcdsaKeypair(
"/path/to/derive",
"/path/to/derive", // This is what the WALLET_SECRET_SALT is used for
"subject-identifier",
);
// evmKeypair can now be used for Ethereum operations
Expand Down
2 changes: 1 addition & 1 deletion packages/plugin-evm/src/providers/wallet.ts
Original file line number Diff line number Diff line change
Expand Up @@ -306,8 +306,8 @@ export const initWalletProvider = async (runtime: IAgentRuntime) => {

const deriveKeyProvider = new DeriveKeyProvider(teeMode);
const deriveKeyResult = await deriveKeyProvider.deriveEcdsaKeypair(
"/",
walletSecretSalt,
"evm",
runtime.agentId
);
return new WalletProvider(
Expand Down
2 changes: 1 addition & 1 deletion packages/plugin-solana/src/keypairUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,8 @@ export async function getWalletKey(

const deriveKeyProvider = new DeriveKeyProvider(teeMode);
const deriveKeyResult = await deriveKeyProvider.deriveEd25519Keypair(
"/",
walletSecretSalt,
"solana",
runtime.agentId
);

Expand Down
3 changes: 2 additions & 1 deletion packages/plugin-tee/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,8 @@
"scripts": {
"build": "tsup --format esm --dts",
"dev": "tsup --format esm --dts --watch",
"lint": "eslint --fix --cache ."
"lint": "eslint --fix --cache .",
"test": "vitest run"
},
"peerDependencies": {
"whatwg-url": "7.1.0"
Expand Down
19 changes: 15 additions & 4 deletions packages/plugin-tee/src/actions/remoteAttestation.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import type { IAgentRuntime, Memory, State, HandlerCallback } from "@elizaos/core";
import { RemoteAttestationProvider } from "../providers/remoteAttestationProvider";
import { fetch, type BodyInit } from "undici";
import { RemoteAttestationMessage } from "../types/tee";

function hexToUint8Array(hex: string) {
hex = hex.trim();
Expand Down Expand Up @@ -42,23 +43,33 @@ export const remoteAttestationAction = {
description: "Generate a remote attestation to prove that the agent is running in a TEE",
handler: async (
runtime: IAgentRuntime,
_message: Memory,
message: Memory,
_state: State,
_options: { [key: string]: unknown },
callback: HandlerCallback,
) => {
try {
// Attestation will be generated based on the message info
const attestationMessage: RemoteAttestationMessage = {
agentId: runtime.agentId,
timestamp: Date.now(),
message: {
userId: message.userId,
roomId: message.roomId,
content: message.content.text,
},
};
// Get the remote attestation of the agentId
const agentId = runtime.agentId;
const teeMode = runtime.getSetting("TEE_MODE");
const provider = new RemoteAttestationProvider(teeMode);
const attestation = await provider.generateAttestation(agentId, 'raw');

const attestation = await provider.generateAttestation(JSON.stringify(attestationMessage));
const attestationData = hexToUint8Array(attestation.quote);
const response = await uploadUint8Array(attestationData);
const data = await response.json();
callback({
text: `Here's my 🧾 RA Quote 🫡
https://proof.t16z.com/reports/${data.checksum}`,
https://proof.t16z.com/reports/${data.checksum}`,
action: "NONE",
});
return true;
Expand Down
35 changes: 26 additions & 9 deletions packages/plugin-tee/src/providers/deriveKeyProvider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,7 @@ import { DeriveKeyResponse, TappdClient } from "@phala/dstack-sdk";
import { privateKeyToAccount } from "viem/accounts";
import { PrivateKeyAccount, keccak256 } from "viem";
import { RemoteAttestationProvider } from "./remoteAttestationProvider";
import { TEEMode, RemoteAttestationQuote } from "../types/tee";

interface DeriveKeyAttestationData {
agentId: string;
publicKey: string;
}
import { TEEMode, RemoteAttestationQuote, DeriveKeyAttestationData } from "../types/tee";

class DeriveKeyProvider {
private client: TappdClient;
Expand Down Expand Up @@ -57,11 +52,13 @@ class DeriveKeyProvider {

private async generateDeriveKeyAttestation(
agentId: string,
publicKey: string
publicKey: string,
subject?: string
): Promise<RemoteAttestationQuote> {
const deriveKeyData: DeriveKeyAttestationData = {
agentId,
publicKey,
subject,
};
const reportdata = JSON.stringify(deriveKeyData);
elizaLogger.log(
Expand All @@ -72,6 +69,12 @@ class DeriveKeyProvider {
return quote;
}

/**
* Derives a raw key from the given path and subject.
* @param path - The path to derive the key from. This is used to derive the key from the root of trust.
* @param subject - The subject to derive the key from. This is used for the certificate chain.
* @returns The derived key.
*/
async rawDeriveKey(
path: string,
subject: string
Expand All @@ -94,6 +97,13 @@ class DeriveKeyProvider {
}
}

/**
* Derives an Ed25519 keypair from the given path and subject.
* @param path - The path to derive the key from. This is used to derive the key from the root of trust.
* @param subject - The subject to derive the key from. This is used for the certificate chain.
* @param agentId - The agent ID to generate an attestation for.
* @returns An object containing the derived keypair and attestation.
*/
async deriveEd25519Keypair(
path: string,
subject: string,
Expand Down Expand Up @@ -130,6 +140,13 @@ class DeriveKeyProvider {
}
}

/**
* Derives an ECDSA keypair from the given path and subject.
* @param path - The path to derive the key from. This is used to derive the key from the root of trust.
* @param subject - The subject to derive the key from. This is used for the certificate chain.
* @param agentId - The agent ID to generate an attestation for. This is used for the certificate chain.
* @returns An object containing the derived keypair and attestation.
*/
async deriveEcdsaKeypair(
path: string,
subject: string,
Expand Down Expand Up @@ -184,13 +201,13 @@ const deriveKeyProvider: Provider = {
const secretSalt =
runtime.getSetting("WALLET_SECRET_SALT") || "secret_salt";
const solanaKeypair = await provider.deriveEd25519Keypair(
"/",
secretSalt,
"solana",
agentId
);
const evmKeypair = await provider.deriveEcdsaKeypair(
"/",
secretSalt,
"evm",
agentId
);
return JSON.stringify({
Expand Down
17 changes: 13 additions & 4 deletions packages/plugin-tee/src/providers/remoteAttestationProvider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import {
elizaLogger,
} from "@elizaos/core";
import { TdxQuoteResponse, TappdClient, TdxQuoteHashAlgorithms } from "@phala/dstack-sdk";
import { RemoteAttestationQuote, TEEMode } from "../types/tee";
import { RemoteAttestationQuote, TEEMode, RemoteAttestationMessage } from "../types/tee";

class RemoteAttestationProvider {
private client: TappdClient;
Expand Down Expand Up @@ -74,14 +74,23 @@ class RemoteAttestationProvider {

// Keep the original provider for backwards compatibility
const remoteAttestationProvider: Provider = {
get: async (runtime: IAgentRuntime, _message: Memory, _state?: State) => {
get: async (runtime: IAgentRuntime, message: Memory, _state?: State) => {
const teeMode = runtime.getSetting("TEE_MODE");
const provider = new RemoteAttestationProvider(teeMode);
const agentId = runtime.agentId;

try {
elizaLogger.log("Generating attestation for: ", agentId);
const attestation = await provider.generateAttestation(agentId, 'raw');
const attestationMessage: RemoteAttestationMessage = {
agentId: agentId,
timestamp: Date.now(),
message: {
userId: message.userId,
roomId: message.roomId,
content: message.content.text,
}
};
elizaLogger.log("Generating attestation for: ", JSON.stringify(attestationMessage));
const attestation = await provider.generateAttestation(JSON.stringify(attestationMessage));
return `Your Agent's remote attestation is: ${JSON.stringify(attestation)}`;
} catch (error) {
console.error("Error in remote attestation provider:", error);
Expand Down
2 changes: 1 addition & 1 deletion packages/plugin-tee/src/providers/walletProvider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -299,8 +299,8 @@ const walletProvider: Provider = {
keypair: Keypair;
attestation: RemoteAttestationQuote;
} = await deriveKeyProvider.deriveEd25519Keypair(
"/",
runtime.getSetting("WALLET_SECRET_SALT"),
"solana",
agentId
);
publicKey = derivedKeyPair.keypair.publicKey;
Expand Down
Loading
Loading