@@ -161,6 +161,16 @@ const (
161161 initDataPath = "/run/peerpod/initdata"
162162)
163163
164+ // appPrivateKeyKey is a reserved Key value that makes the helper emit the
165+ // threshold-recovered app_private_key itself (hex of its compressed G1 bytes)
166+ // rather than an environment variable. This is the KMS-derived root a
167+ // signing daemon seeds from (app_private_key = S·H(appID)); it travels the
168+ // exact same eigenx-snp attestation path as a sealed env var, so it is only
169+ // ever recoverable inside an attested TEE. Because it is not part of the
170+ // release env, it bypasses the merged-env assembly, the IBE-decrypt, and the
171+ // tmpfs env cache entirely (the root must never be written to disk).
172+ const appPrivateKeyKey = "__EIGENX_APP_PRIVATE_KEY__"
173+
164174func main () {
165175 if err := run (); err != nil {
166176 log .Printf ("kmsCDHHelper: %v" , err )
@@ -180,10 +190,15 @@ func run() error {
180190 // is held across the read so we don't race a concurrent first-call writer
181191 // (kata-agent unseals sequentially today, but a multi-container pod shares
182192 // the podVM). See env_cache.go.
183- if env , ok , cerr := loadCachedEnv (req .AppID ); cerr != nil {
184- return fmt .Errorf ("read env cache: %w" , cerr )
185- } else if ok {
186- return emitKey (env , req .Key )
193+ //
194+ // The app_private_key root is never cached (it is not part of the env map),
195+ // so its request always attests fresh and never consults the cache.
196+ if req .Key != appPrivateKeyKey {
197+ if env , ok , cerr := loadCachedEnv (req .AppID ); cerr != nil {
198+ return fmt .Errorf ("read env cache: %w" , cerr )
199+ } else if ok {
200+ return emitKey (env , req .Key )
201+ }
187202 }
188203
189204 rsaPriv , rsaPubPEM , err := generateRSAKeypair ()
@@ -251,8 +266,14 @@ func run() error {
251266 // Cache the whole merged env so sibling sealed vars for this app this pod
252267 // boot hit the fast path above. Best-effort: a cache write failure must not
253268 // fail the unseal — we already have the value in hand.
254- if cerr := storeCachedEnv (req .AppID , env ); cerr != nil {
255- log .Printf ("warning: cache merged env for app %q: %v" , req .AppID , cerr )
269+ //
270+ // Never cache the app_private_key root: it is not part of the release env,
271+ // and it must not be persisted to the tmpfs cache. Its request always
272+ // re-attests (see the fast-path guard above).
273+ if req .Key != appPrivateKeyKey {
274+ if cerr := storeCachedEnv (req .AppID , env ); cerr != nil {
275+ log .Printf ("warning: cache merged env for app %q: %v" , req .AppID , cerr )
276+ }
256277 }
257278
258279 return emitKey (env , req .Key )
@@ -653,6 +674,21 @@ func retrieveAndDecrypt(
653674 return nil , fmt .Errorf ("RetrieveSecretsWithOptions: %w" , err )
654675 }
655676
677+ // Root-key request: emit the threshold-recovered app_private_key itself
678+ // (hex of its compressed G1 bytes) and stop. This is the KMS-derived root a
679+ // signing daemon seeds from; it does not depend on the release's
680+ // encrypted_env, so we return before the IBE-decrypt below. The key was
681+ // already validated against the master public key by RetrieveSecretsWithOptions
682+ // (VerifyAppPrivateKey), so a poisoned KMS fails there, not here.
683+ if req .Key == appPrivateKeyKey {
684+ if len (result .AppPrivateKey .CompressedBytes ) == 0 {
685+ return nil , fmt .Errorf ("KMS returned empty app_private_key for app %q" , req .AppID )
686+ }
687+ return map [string ]string {
688+ appPrivateKeyKey : hex .EncodeToString (result .AppPrivateKey .CompressedBytes ),
689+ }, nil
690+ }
691+
656692 // Per the KMS design (docs/references/new_kms.md, "Application Decryption"):
657693 // the secret is the on-chain release's encrypted_env, returned in the
658694 // /secrets response and IBE-decrypted here with the threshold-recovered
0 commit comments