Skip to content

Commit cf52392

Browse files
committed
refactor(sdk): remove RTMR replay helpers from client SDKs
The SDKs are attester-side tools for obtaining attestation evidence from inside a CVM; replaying RTMRs is a relying-party concern and does not belong here. The helpers were also unsound as shipped: get_quote strips runtime-event digests (see TdxEvent::stripped), so replaying RTMR3 from the returned event_log could not reproduce the quoted value. Removes replay_rtmrs / replayRtmrs / ReplayRTMRs and their private replay_rtmr helpers from the Rust, Python, JS and Go SDKs, including the legacy tappd clients, plus the associated tests, examples and docs. Drops the now-unused sha2 dependency from dstack-sdk-types. BREAKING CHANGE: consumers replaying RTMRs must do so against a verifier that has the unstripped event log.
1 parent 50072cf commit cf52392

21 files changed

Lines changed: 11 additions & 436 deletions

sdk/go/README.md

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -123,13 +123,6 @@ func main() {
123123

124124
fmt.Println("TDX Quote:", quote.Quote)
125125
fmt.Println("Event Log:", quote.EventLog)
126-
127-
// Verify measurement registers
128-
rtmrs, err := quote.ReplayRTMRs()
129-
if err != nil {
130-
log.Fatal(err)
131-
}
132-
fmt.Println("RTMR0-3:", rtmrs)
133126
}
134127
```
135128

@@ -574,7 +567,6 @@ Generates a TDX attestation quote containing the provided report data.
574567
**Returns:** `GetQuoteResponse`
575568
- `Quote`: TDX quote as hex string
576569
- `EventLog`: JSON string of system events
577-
- `ReplayRTMRs()`: Function returning computed RTMR values
578570

579571
**Use Cases:**
580572
- Remote attestation of application state

sdk/go/dstack/client.go

Lines changed: 0 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@ import (
1111
"context"
1212
"crypto/ecdsa"
1313
"crypto/ed25519"
14-
"crypto/sha512"
1514
"crypto/x509"
1615
"encoding/hex"
1716
"encoding/json"
@@ -114,7 +113,6 @@ func (r *GetQuoteResponse) DecodeEventLog() ([]EventLog, error) {
114113
return events, err
115114
}
116115

117-
118116
// Represents the response from an attestation request.
119117
type AttestResponse struct {
120118
Attestation []byte
@@ -187,63 +185,6 @@ func (r *InfoResponse) DecodeTcbInfo() (*TcbInfo, error) {
187185
return &tcbInfo, nil
188186
}
189187

190-
const INIT_MR = "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"
191-
192-
// Replays the RTMR history to calculate final RTMR values
193-
func replayRTMR(history []string) (string, error) {
194-
if len(history) == 0 {
195-
return INIT_MR, nil
196-
}
197-
198-
mr := make([]byte, 48)
199-
200-
for _, content := range history {
201-
contentBytes, err := hex.DecodeString(content)
202-
if err != nil {
203-
return "", err
204-
}
205-
206-
if len(contentBytes) < 48 {
207-
padding := make([]byte, 48-len(contentBytes))
208-
contentBytes = append(contentBytes, padding...)
209-
}
210-
211-
h := sha512.New384()
212-
h.Write(append(mr, contentBytes...))
213-
mr = h.Sum(nil)
214-
}
215-
216-
return hex.EncodeToString(mr), nil
217-
}
218-
219-
// Replays the RTMR history to calculate final RTMR values
220-
func (r *GetQuoteResponse) ReplayRTMRs() (map[int]string, error) {
221-
var eventLog []struct {
222-
IMR int `json:"imr"`
223-
Digest string `json:"digest"`
224-
}
225-
json.Unmarshal([]byte(r.EventLog), &eventLog)
226-
227-
rtmrs := make(map[int]string, 4)
228-
for idx := 0; idx < 4; idx++ {
229-
history := make([]string, 0)
230-
for _, event := range eventLog {
231-
if event.IMR == idx {
232-
history = append(history, event.Digest)
233-
}
234-
}
235-
236-
rtmr, err := replayRTMR(history)
237-
if err != nil {
238-
return nil, err
239-
}
240-
241-
rtmrs[idx] = rtmr
242-
}
243-
244-
return rtmrs, nil
245-
}
246-
247188
// QuoteHashAlgorithm represents the hash algorithm used for quote generation
248189
type QuoteHashAlgorithm string
249190

sdk/go/dstack/client_test.go

Lines changed: 0 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@ import (
99
"context"
1010
"crypto/sha256"
1111
"crypto/x509"
12-
"encoding/hex"
1312
"encoding/json"
1413
"encoding/pem"
1514
"fmt"
@@ -57,45 +56,6 @@ func TestGetQuote(t *testing.T) {
5756
if err != nil {
5857
t.Errorf("expected event log to be a valid JSON object: %v", err)
5958
}
60-
61-
// Get quote RTMRs manually
62-
quoteBytes, err := resp.DecodeQuote()
63-
if err != nil {
64-
t.Fatal(err)
65-
}
66-
67-
quoteRtmrs := [4][48]byte{
68-
[48]byte(quoteBytes[376:424]),
69-
[48]byte(quoteBytes[424:472]),
70-
[48]byte(quoteBytes[472:520]),
71-
[48]byte(quoteBytes[520:568]),
72-
}
73-
74-
// Test ReplayRTMRs
75-
rtmrs, err := resp.ReplayRTMRs()
76-
if err != nil {
77-
t.Fatal(err)
78-
}
79-
80-
if len(rtmrs) != 4 {
81-
t.Errorf("expected 4 RTMRs, got %d", len(rtmrs))
82-
}
83-
84-
// Verify RTMRs
85-
for i := 0; i < 4; i++ {
86-
if rtmrs[i] == "" {
87-
t.Errorf("expected RTMR %d to not be empty", i)
88-
}
89-
90-
rtmrBytes, err := hex.DecodeString(rtmrs[i])
91-
if err != nil {
92-
t.Errorf("expected RTMR %d to be valid hex: %v", i, err)
93-
}
94-
95-
if !bytes.Equal(rtmrBytes, quoteRtmrs[i][:]) {
96-
t.Errorf("expected RTMR %d to be %s, got %s", i, hex.EncodeToString(quoteRtmrs[i][:]), rtmrs[i])
97-
}
98-
}
9959
}
10060

10161
func TestAttest(t *testing.T) {

sdk/go/tappd/client.go

Lines changed: 0 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@ package tappd
1111
import (
1212
"bytes"
1313
"context"
14-
"crypto/sha512"
1514
"encoding/base64"
1615
"encoding/hex"
1716
"encoding/json"
@@ -100,63 +99,6 @@ type TappdInfoResponse struct {
10099
AppName string `json:"app_name"`
101100
}
102101

103-
const INIT_MR = "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"
104-
105-
// Replays the RTMR history to calculate final RTMR values
106-
func replayRTMR(history []string) (string, error) {
107-
if len(history) == 0 {
108-
return INIT_MR, nil
109-
}
110-
111-
mr := make([]byte, 48)
112-
113-
for _, content := range history {
114-
contentBytes, err := hex.DecodeString(content)
115-
if err != nil {
116-
return "", err
117-
}
118-
119-
if len(contentBytes) < 48 {
120-
padding := make([]byte, 48-len(contentBytes))
121-
contentBytes = append(contentBytes, padding...)
122-
}
123-
124-
h := sha512.New384()
125-
h.Write(append(mr, contentBytes...))
126-
mr = h.Sum(nil)
127-
}
128-
129-
return hex.EncodeToString(mr), nil
130-
}
131-
132-
// Replays the RTMR history to calculate final RTMR values
133-
func (r *TdxQuoteResponse) ReplayRTMRs() (map[int]string, error) {
134-
var eventLog []struct {
135-
IMR int `json:"imr"`
136-
Digest string `json:"digest"`
137-
}
138-
json.Unmarshal([]byte(r.EventLog), &eventLog)
139-
140-
rtmrs := make(map[int]string, 4)
141-
for idx := 0; idx < 4; idx++ {
142-
history := make([]string, 0)
143-
for _, event := range eventLog {
144-
if event.IMR == idx {
145-
history = append(history, event.Digest)
146-
}
147-
}
148-
149-
rtmr, err := replayRTMR(history)
150-
if err != nil {
151-
return nil, err
152-
}
153-
154-
rtmrs[idx] = rtmr
155-
}
156-
157-
return rtmrs, nil
158-
}
159-
160102
// Handles communication with the Tappd service.
161103
type TappdClient struct {
162104
endpoint string

sdk/go/tappd/client_test.go

Lines changed: 1 addition & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@
55
package tappd_test
66

77
import (
8-
"bytes"
98
"context"
109
"encoding/hex"
1110
"encoding/json"
@@ -70,44 +69,10 @@ func TestTdxQuote(t *testing.T) {
7069
t.Errorf("expected event log to be a valid JSON object: %v", err)
7170
}
7271

73-
quoteBytes, err := hex.DecodeString(resp.Quote)
72+
_, err = hex.DecodeString(resp.Quote)
7473
if err != nil {
7574
t.Errorf("expected quote to be a valid hex string: %v", err)
7675
}
77-
78-
// Get quote RTMRs manually
79-
quoteRtmrs := [4][48]byte{
80-
[48]byte(quoteBytes[376:424]),
81-
[48]byte(quoteBytes[424:472]),
82-
[48]byte(quoteBytes[472:520]),
83-
[48]byte(quoteBytes[520:568]),
84-
}
85-
86-
// Test ReplayRTMRs
87-
rtmrs, err := resp.ReplayRTMRs()
88-
if err != nil {
89-
t.Fatal(err)
90-
}
91-
92-
if len(rtmrs) != 4 {
93-
t.Errorf("expected 4 RTMRs, got %d", len(rtmrs))
94-
}
95-
96-
// Verify RTMRs
97-
for i := 0; i < 4; i++ {
98-
if rtmrs[i] == "" {
99-
t.Errorf("expected RTMR %d to not be empty", i)
100-
}
101-
102-
rtmrBytes, err := hex.DecodeString(rtmrs[i])
103-
if err != nil {
104-
t.Errorf("expected RTMR %d to be valid hex: %v", i, err)
105-
}
106-
107-
if !bytes.Equal(rtmrBytes, quoteRtmrs[i][:]) {
108-
t.Errorf("expected RTMR %d to be %s, got %s", i, hex.EncodeToString(quoteRtmrs[i][:]), rtmrs[i])
109-
}
110-
}
11176
}
11277

11378
func TestTdxQuoteRawHash(t *testing.T) {

sdk/js/README.md

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ console.log(Buffer.from(key.key).toString('hex'))
3333

3434
const quote = await client.getQuote('app-state-snapshot')
3535
console.log(quote.quote)
36-
console.log(quote.replayRtmrs())
36+
console.log(quote.event_log)
3737
```
3838

3939
The constructor probes `/var/run/dstack.sock`, then `/run/dstack.sock`, then the `/var/run/dstack/` and `/run/dstack/` variants. Pass an explicit endpoint for HTTP or for a non-default socket:
@@ -86,7 +86,6 @@ Generate a raw TDX quote. `reportData` is up to 64 bytes (string, Buffer, or Uin
8686
const quote = await client.getQuote('user:alice:nonce123')
8787
quote.quote // hex-encoded TDX quote
8888
quote.event_log // JSON string of measured events
89-
quote.replayRtmrs() // recompute RTMR[0..3] from the event log
9089
```
9190

9291
### `attest(reportData)`

sdk/js/src/__tests__/index.test.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,6 @@ describe('DstackClient', () => {
4646
expect(result).toHaveProperty('event_log')
4747
expect(result.event_log.substring(0, 1) === '{')
4848
expect(() => JSON.parse(result.event_log)).not.toThrowError()
49-
expect(result.replayRtmrs().length).toBe(4)
5049
})
5150

5251
it('should be able to attest', async () => {

sdk/js/src/index.ts

Lines changed: 0 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@
33
// SPDX-License-Identifier: Apache-2.0
44

55
import fs from 'fs'
6-
import { sha384 } from '@noble/hashes/sha512'
76
import { send_rpc_request } from './send-rpc-request'
87
export { getComposeHash } from './get-compose-hash'
98
export { verifyEnvEncryptPublicKey, verifyEnvEncryptPublicKeyLegacy } from './verify-env-encrypt-public-key'
@@ -101,8 +100,6 @@ export interface GetQuoteResponse {
101100
report_data?: Hex
102101
vm_config?: string
103102
attestation?: Hex
104-
105-
replayRtmrs: () => string[]
106103
}
107104

108105
export interface AttestResponse {
@@ -149,36 +146,6 @@ function x509key_to_uint8array(pem: string, max_length?: number) {
149146
return result
150147
}
151148

152-
function replay_rtmr(history: string[]): string {
153-
const INIT_MR = "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"
154-
if (history.length === 0) {
155-
return INIT_MR
156-
}
157-
let mr = Buffer.from(INIT_MR, 'hex')
158-
for (const content of history) {
159-
// Convert hex string to buffer
160-
let contentBuffer = Buffer.from(content, 'hex')
161-
// Pad content with zeros if shorter than 48 bytes
162-
if (contentBuffer.length < 48) {
163-
const padding = Buffer.alloc(48 - contentBuffer.length, 0)
164-
contentBuffer = Buffer.concat([contentBuffer, padding])
165-
}
166-
mr = Buffer.from(sha384(Buffer.concat([mr, contentBuffer])))
167-
}
168-
return mr.toString('hex')
169-
}
170-
171-
function reply_rtmrs(event_log: EventLog[]): Record<number, string> {
172-
const rtmrs: Array<string> = []
173-
for (let idx = 0; idx < 4; idx++) {
174-
const history = event_log
175-
.filter(event => event.imr === idx)
176-
.map(event => event.digest)
177-
rtmrs[idx] = replay_rtmr(history)
178-
}
179-
return rtmrs
180-
}
181-
182149
export interface TlsKeyOptions {
183150
path?: string;
184151
subject?: string;
@@ -312,11 +279,6 @@ export class DstackClient<T extends TcbInfo = TcbInfoV05x> {
312279
const err = result['error'] as string
313280
throw new Error(err)
314281
}
315-
Object.defineProperty(result, 'replayRtmrs', {
316-
get: () => () => reply_rtmrs(JSON.parse(result.event_log) as EventLog[]),
317-
enumerable: true,
318-
configurable: false,
319-
})
320282
return Object.freeze(result)
321283
}
322284

@@ -531,11 +493,6 @@ export class TappdClient extends DstackClient<TcbInfoV03x> {
531493
const err = result['error'] as string
532494
throw new Error(err)
533495
}
534-
Object.defineProperty(result, 'replayRtmrs', {
535-
get: () => () => reply_rtmrs(JSON.parse(result.event_log) as EventLog[]),
536-
enumerable: true,
537-
configurable: false,
538-
})
539496
return Object.freeze(result)
540497
}
541498

0 commit comments

Comments
 (0)