Skip to content

Commit 23d39e7

Browse files
committed
feat(kms): add GetAttestationInfo RPC to onboard service
Adds a new GetAttestationInfo RPC endpoint to the Onboard service that returns the real device_id, mr_aggregated, os_image_hash, and mr_system values needed for on-chain KMS authorization registration. The endpoint verifies the TDX attestation quote via PCCS to obtain the real platform device identifier (ppid), which is required to compute the correct device_id (SHA256 of ppid). This differs from the serial log values which use an empty ppid. Also updates the onboard web UI to auto-load and display the attestation info on page load.
1 parent de90047 commit 23d39e7

File tree

3 files changed

+140
-2
lines changed

3 files changed

+140
-2
lines changed

kms/rpc/proto/kms_rpc.proto

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -133,12 +133,28 @@ message OnboardRequest {
133133
message OnboardResponse {
134134
}
135135

136+
// Attestation info needed for on-chain KMS authorization.
137+
message AttestationInfoResponse {
138+
// Device ID (SHA256 of platform device identifier)
139+
bytes device_id = 1;
140+
// Aggregated measurement of the VM execution environment
141+
bytes mr_aggregated = 2;
142+
// OS image hash
143+
bytes os_image_hash = 3;
144+
// System measurement
145+
bytes mr_system = 4;
146+
// Attestation mode (e.g. "dstack-tdx", "dstack-gcp-tdx")
147+
string attestation_mode = 5;
148+
}
149+
136150
// The Onboard RPC service.
137151
service Onboard {
138152
// Bootstrap a new KMS
139153
rpc Bootstrap(BootstrapRequest) returns (BootstrapResponse);
140154
// Onboard from existing KMS
141155
rpc Onboard(OnboardRequest) returns (OnboardResponse);
156+
// Get attestation info for on-chain KMS authorization
157+
rpc GetAttestationInfo(google.protobuf.Empty) returns (AttestationInfoResponse);
142158
// Finish onboarding
143159
rpc Finish(google.protobuf.Empty) returns (google.protobuf.Empty);
144160
}

kms/src/onboard_service.rs

Lines changed: 44 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,8 @@ use dstack_guest_agent_rpc::{
99
use dstack_kms_rpc::{
1010
kms_client::KmsClient,
1111
onboard_server::{OnboardRpc, OnboardServer},
12-
BootstrapRequest, BootstrapResponse, GetKmsKeyRequest, OnboardRequest, OnboardResponse,
12+
AttestationInfoResponse, BootstrapRequest, BootstrapResponse, GetKmsKeyRequest,
13+
OnboardRequest, OnboardResponse,
1314
};
1415
use fs_err as fs;
1516
use http_client::prpc::PrpcClient;
@@ -90,6 +91,48 @@ impl OnboardRpc for OnboardHandler {
9091
Ok(OnboardResponse {})
9192
}
9293

94+
async fn get_attestation_info(self) -> Result<AttestationInfoResponse> {
95+
let pccs_url = self.state.config.pccs_url.clone();
96+
97+
// Get attestation from guest agent
98+
let report_data = pad64([0u8; 32]);
99+
let response = app_attest(report_data)
100+
.await
101+
.context("Failed to get attestation")?;
102+
103+
// Decode and verify the attestation to get real device ID
104+
let attestation = VersionedAttestation::from_scale(&response.attestation)
105+
.context("Failed to decode attestation")?
106+
.into_inner();
107+
let attestation_mode = serde_json::to_value(attestation.quote.mode())
108+
.ok()
109+
.and_then(|v| v.as_str().map(String::from))
110+
.unwrap_or_else(|| format!("{:?}", attestation.quote.mode()));
111+
let verified = attestation
112+
.verify(pccs_url.as_deref())
113+
.await
114+
.context("Failed to verify attestation")?;
115+
116+
// Get vm_config from guest agent
117+
let info = dstack_client()
118+
.info()
119+
.await
120+
.context("Failed to get VM info")?;
121+
122+
// Decode app info to get device_id, mr_aggregated, os_image_hash, mr_system
123+
let app_info = verified
124+
.decode_app_info_ex(false, &info.vm_config)
125+
.context("Failed to decode app info")?;
126+
127+
Ok(AttestationInfoResponse {
128+
device_id: app_info.device_id,
129+
mr_aggregated: app_info.mr_aggregated.to_vec(),
130+
os_image_hash: app_info.os_image_hash,
131+
mr_system: app_info.mr_system.to_vec(),
132+
attestation_mode,
133+
})
134+
}
135+
93136
async fn finish(self) -> anyhow::Result<()> {
94137
std::process::exit(0);
95138
}

kms/src/www/onboard.html

Lines changed: 80 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -113,13 +113,75 @@
113113
.fade-leave-to {
114114
opacity: 0;
115115
}
116+
117+
.attestation-info {
118+
background-color: #f0f4f8;
119+
border: 1px solid #ccd;
120+
border-radius: 4px;
121+
padding: 15px;
122+
margin-bottom: 20px;
123+
}
124+
125+
.attestation-info h3 {
126+
margin-top: 0;
127+
color: #444;
128+
}
129+
130+
.info-row {
131+
display: flex;
132+
margin-bottom: 8px;
133+
font-size: 0.9em;
134+
}
135+
136+
.info-label {
137+
font-weight: bold;
138+
min-width: 160px;
139+
color: #555;
140+
}
141+
142+
.info-value {
143+
font-family: monospace;
144+
word-break: break-all;
145+
color: #333;
146+
}
147+
148+
.loading {
149+
color: #888;
150+
font-style: italic;
151+
}
116152
</style>
117153
</head>
118154

119155
<body>
120156
<div id="app" class="container">
121157
<h1>dstack KMS Setup</h1>
122158

159+
<div v-if="attestationLoading" class="loading">Loading attestation info...</div>
160+
<div v-else-if="attestationError" class="error">Attestation info: {{ attestationError }}</div>
161+
<div v-else-if="attestationInfo" class="attestation-info">
162+
<h3>Attestation Info (for on-chain registration)</h3>
163+
<div class="info-row">
164+
<span class="info-label">Attestation Mode:</span>
165+
<span class="info-value">{{ attestationInfo.attestation_mode }}</span>
166+
</div>
167+
<div class="info-row">
168+
<span class="info-label">Device ID:</span>
169+
<span class="info-value">0x{{ attestationInfo.device_id }}</span>
170+
</div>
171+
<div class="info-row">
172+
<span class="info-label">MR Aggregated:</span>
173+
<span class="info-value">0x{{ attestationInfo.mr_aggregated }}</span>
174+
</div>
175+
<div class="info-row">
176+
<span class="info-label">OS Image Hash:</span>
177+
<span class="info-value">0x{{ attestationInfo.os_image_hash }}</span>
178+
</div>
179+
<div class="info-row">
180+
<span class="info-label">MR System:</span>
181+
<span class="info-value">0x{{ attestationInfo.mr_system }}</span>
182+
</div>
183+
</div>
184+
123185
<div v-if="!setupFinished">
124186
<div v-if="!selectedOption" class="initial-buttons">
125187
<button @click="selectedOption = 'bootstrap'">Bootstrap</button>
@@ -200,7 +262,24 @@ <h2>Onboard from an Existing KMS Instance</h2>
200262
error: '',
201263
success: '',
202264
result: '',
203-
setupFinished: false
265+
setupFinished: false,
266+
attestationInfo: null,
267+
attestationLoading: true,
268+
attestationError: ''
269+
}
270+
},
271+
async mounted() {
272+
try {
273+
const data = await rpcCall('GetAttestationInfo', {});
274+
if (data.error) {
275+
this.attestationError = data.error;
276+
} else {
277+
this.attestationInfo = data;
278+
}
279+
} catch (err) {
280+
this.attestationError = err.message;
281+
} finally {
282+
this.attestationLoading = false;
204283
}
205284
},
206285
methods: {

0 commit comments

Comments
 (0)