mirror of
https://github.com/ultravioletrs/cocos.git
synced 2026-06-23 04:10:25 +00:00
fix: Implement KBS RCAR handshake with cookies
Fixes 'cookie not found' error (401) from KBS by: 1. Adding CookieJar support to KBS client 2. Implementing GetChallenge() to perform /auth handshake and capture session cookie 3. Updating Agent to get challenge, decode nonce, and use it for evidence generation 4. Regenerating mocks
This commit is contained in:
+24
-7
@@ -533,6 +533,26 @@ func (as *agentService) downloadAndDecryptResource(ctx context.Context, source *
|
|||||||
// 2. Get TEE evidence for KBS attestation
|
// 2. Get TEE evidence for KBS attestation
|
||||||
as.logger.Info("getting TEE evidence for KBS attestation")
|
as.logger.Info("getting TEE evidence for KBS attestation")
|
||||||
|
|
||||||
|
// Initialize KBS client first to get the challenge (nonce)
|
||||||
|
as.logger.Info("initiating KBS attestation handshake", "kbs_url", as.computation.KBS.URL)
|
||||||
|
kbsClient := kbs.NewClient(kbs.Config{
|
||||||
|
URL: as.computation.KBS.URL,
|
||||||
|
Timeout: 30 * time.Second,
|
||||||
|
})
|
||||||
|
|
||||||
|
// Start RCAR handshake - get nonce and establish session cookie
|
||||||
|
nonceStr, err := kbsClient.GetChallenge(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to get KBS challenge: %w", err)
|
||||||
|
}
|
||||||
|
as.logger.Info("received KBS challenge nonce")
|
||||||
|
|
||||||
|
// Decode nonce for Attestation Service
|
||||||
|
nonceBytes, err := base64.StdEncoding.DecodeString(nonceStr)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to decode KBS nonce: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
// Auto-detect the platform type
|
// Auto-detect the platform type
|
||||||
platform := attestation.CCPlatform()
|
platform := attestation.CCPlatform()
|
||||||
as.logger.Info("detected platform type", "platform", platform)
|
as.logger.Info("detected platform type", "platform", platform)
|
||||||
@@ -542,6 +562,8 @@ func (as *agentService) downloadAndDecryptResource(ctx context.Context, source *
|
|||||||
|
|
||||||
// Use computation ID as report data
|
// Use computation ID as report data
|
||||||
copy(reportData[:], []byte(as.computation.ID))
|
copy(reportData[:], []byte(as.computation.ID))
|
||||||
|
// Use KBS provided nonce
|
||||||
|
copy(nonce[:], nonceBytes)
|
||||||
|
|
||||||
var kbsEvidence []byte
|
var kbsEvidence []byte
|
||||||
|
|
||||||
@@ -574,15 +596,10 @@ func (as *agentService) downloadAndDecryptResource(ctx context.Context, source *
|
|||||||
as.logger.Info("evidence prepared for KBS", "kbs_evidence_len", len(kbsEvidence))
|
as.logger.Info("evidence prepared for KBS", "kbs_evidence_len", len(kbsEvidence))
|
||||||
|
|
||||||
// 3. Attest with KBS and get token
|
// 3. Attest with KBS and get token
|
||||||
as.logger.Info("attesting with KBS", "kbs_url", as.computation.KBS.URL)
|
as.logger.Info("attesting with KBS")
|
||||||
|
|
||||||
kbsClient := kbs.NewClient(kbs.Config{
|
|
||||||
URL: as.computation.KBS.URL,
|
|
||||||
Timeout: 30 * time.Second,
|
|
||||||
})
|
|
||||||
|
|
||||||
runtimeData := kbs.RuntimeData{
|
runtimeData := kbs.RuntimeData{
|
||||||
Nonce: base64.StdEncoding.EncodeToString(nonce[:]),
|
Nonce: nonceStr,
|
||||||
}
|
}
|
||||||
|
|
||||||
token, err := kbsClient.Attest(ctx, kbsEvidence, runtimeData)
|
token, err := kbsClient.Attest(ctx, kbsEvidence, runtimeData)
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"net/http/cookiejar"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/absmach/supermq/pkg/errors"
|
"github.com/absmach/supermq/pkg/errors"
|
||||||
@@ -26,6 +27,9 @@ var (
|
|||||||
|
|
||||||
// Client defines the interface for KBS (Key Broker Service) communication.
|
// Client defines the interface for KBS (Key Broker Service) communication.
|
||||||
type Client interface {
|
type Client interface {
|
||||||
|
// GetChallenge initiates the attestation handshake and returns the nonce.
|
||||||
|
GetChallenge(ctx context.Context) (string, error)
|
||||||
|
|
||||||
// Attest performs attestation with KBS and returns a token.
|
// Attest performs attestation with KBS and returns a token.
|
||||||
Attest(ctx context.Context, evidence []byte, runtimeData RuntimeData) (string, error)
|
Attest(ctx context.Context, evidence []byte, runtimeData RuntimeData) (string, error)
|
||||||
|
|
||||||
@@ -86,14 +90,57 @@ func NewClient(config Config) Client {
|
|||||||
config.Timeout = 30 * time.Second
|
config.Timeout = 30 * time.Second
|
||||||
}
|
}
|
||||||
|
|
||||||
|
jar, _ := cookiejar.New(nil)
|
||||||
|
|
||||||
return &kbsClient{
|
return &kbsClient{
|
||||||
config: config,
|
config: config,
|
||||||
client: &http.Client{
|
client: &http.Client{
|
||||||
Timeout: config.Timeout,
|
Timeout: config.Timeout,
|
||||||
|
Jar: jar,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetChallenge initiates the RCAR handshake calling /kbs/v0/auth
|
||||||
|
func (c *kbsClient) GetChallenge(ctx context.Context) (string, error) {
|
||||||
|
url := fmt.Sprintf("%s/kbs/v0/auth", c.config.URL)
|
||||||
|
|
||||||
|
authReq := AuthRequest{
|
||||||
|
Version: "0.1.0",
|
||||||
|
TEE: "sample", // Initial handshake can be generic or specific
|
||||||
|
}
|
||||||
|
|
||||||
|
reqBody, err := json.Marshal(authReq)
|
||||||
|
if err != nil {
|
||||||
|
return "", errors.Wrap(ErrAttestationFailed, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
req, err := http.NewRequestWithContext(ctx, http.MethodPost, url, bytes.NewReader(reqBody))
|
||||||
|
if err != nil {
|
||||||
|
return "", errors.Wrap(ErrAttestationFailed, err)
|
||||||
|
}
|
||||||
|
req.Header.Set("Content-Type", "application/json")
|
||||||
|
|
||||||
|
resp, err := c.client.Do(req)
|
||||||
|
if err != nil {
|
||||||
|
return "", errors.Wrap(ErrAttestationFailed, err)
|
||||||
|
}
|
||||||
|
defer resp.Body.Close()
|
||||||
|
|
||||||
|
if resp.StatusCode != http.StatusOK {
|
||||||
|
body, _ := io.ReadAll(resp.Body)
|
||||||
|
return "", errors.Wrap(ErrAttestationFailed,
|
||||||
|
fmt.Errorf("auth HTTP %d: %s", resp.StatusCode, string(body)))
|
||||||
|
}
|
||||||
|
|
||||||
|
var authResp AuthResponse
|
||||||
|
if err := json.NewDecoder(resp.Body).Decode(&authResp); err != nil {
|
||||||
|
return "", errors.Wrap(ErrInvalidResponse, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return authResp.Nonce, nil
|
||||||
|
}
|
||||||
|
|
||||||
// Attest performs attestation with KBS and returns a token.
|
// Attest performs attestation with KBS and returns a token.
|
||||||
func (c *kbsClient) Attest(ctx context.Context, evidence []byte, runtimeData RuntimeData) (string, error) {
|
func (c *kbsClient) Attest(ctx context.Context, evidence []byte, runtimeData RuntimeData) (string, error) {
|
||||||
// Build attest request
|
// Build attest request
|
||||||
@@ -115,6 +162,7 @@ func (c *kbsClient) Attest(ctx context.Context, evidence []byte, runtimeData Run
|
|||||||
}
|
}
|
||||||
req.Header.Set("Content-Type", "application/json")
|
req.Header.Set("Content-Type", "application/json")
|
||||||
|
|
||||||
|
// Cookie jar handles the session cookie automatically
|
||||||
resp, err := c.client.Do(req)
|
resp, err := c.client.Do(req)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", errors.Wrap(ErrAttestationFailed, err)
|
return "", errors.Wrap(ErrAttestationFailed, err)
|
||||||
@@ -123,6 +171,7 @@ func (c *kbsClient) Attest(ctx context.Context, evidence []byte, runtimeData Run
|
|||||||
|
|
||||||
if resp.StatusCode != http.StatusOK {
|
if resp.StatusCode != http.StatusOK {
|
||||||
body, _ := io.ReadAll(resp.Body)
|
body, _ := io.ReadAll(resp.Body)
|
||||||
|
// Try to parse error details if JSON
|
||||||
return "", errors.Wrap(ErrAttestationFailed,
|
return "", errors.Wrap(ErrAttestationFailed,
|
||||||
fmt.Errorf("HTTP %d: %s", resp.StatusCode, string(body)))
|
fmt.Errorf("HTTP %d: %s", resp.StatusCode, string(body)))
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user