COCOS-407 - Add support for Linux IMA (#429)

* Added a feature which enables users to fetch IMA measurements and verify them

* Added a feature which enables users to fetch IMA measurements and verify them

* fixed lint error

* fixed according to comments

* fixed according to comments

* fixed according to comments

* fixed according to comments

* final bug fix
This commit is contained in:
Jovan Djukic
2025-05-14 13:03:56 +02:00
committed by GitHub
parent 93f2f2ab46
commit 5c60bc2a48
20 changed files with 675 additions and 49 deletions
+28
View File
@@ -33,6 +33,7 @@ const (
eventLog = "/sys/kernel/security/tpm0/binary_bios_measurements"
Nonce = 32
PCR15 = 15
Hash1 = 20
Hash256 = 32
Hash384 = 48
)
@@ -315,3 +316,30 @@ func calculatePCRTLSKey(pubKey []byte) ([]byte, []byte) {
return newPcr256[:], newPcr384[:]
}
func getPCRValue(index int, algorithm tpm2.Algorithm) ([]byte, error) {
rwc, err := OpenTpm()
if err != nil {
return nil, err
}
defer rwc.Close()
pcrValue, err := tpm2.ReadPCR(rwc, index, algorithm)
if err != nil {
return nil, err
}
return pcrValue, nil
}
func GetPCRSHA1Value(index int) ([]byte, error) {
return getPCRValue(index, tpm2.AlgSHA1)
}
func GetPCRSHA256Value(index int) ([]byte, error) {
return getPCRValue(index, tpm2.AlgSHA256)
}
func GetPCRSHA384Value(index int) ([]byte, error) {
return getPCRValue(index, tpm2.AlgSHA384)
}
+17
View File
@@ -10,6 +10,7 @@ import (
"github.com/fatih/color"
"github.com/ultravioletrs/cocos/agent"
"github.com/ultravioletrs/cocos/pkg/attestation/vtpm"
"golang.org/x/term"
)
@@ -338,6 +339,22 @@ func (p *ProgressBar) ReceiveResult(description string, totalSize int, stream ag
}, resultFile)
}
func (p *ProgressBar) ReceiveIMAMeasurements(description string, totalSize int, stream agent.AgentService_IMAMeasurementsClient, resultFile *os.File) ([]byte, error) {
pcr10 := make([]byte, vtpm.Hash1)
err := p.receiveStream(description, totalSize, func() ([]byte, error) {
response, err := stream.Recv()
if err != nil {
return nil, err
}
copy(pcr10, response.Pcr10[:20])
return response.File, nil
}, resultFile)
return pcr10, err
}
func (p *ProgressBar) ReceiveAttestation(description string, totalSize int, stream agent.AgentService_AttestationClient, attestationFile *os.File) error {
return p.receiveStream(description, totalSize, func() ([]byte, error) {
response, err := stream.Recv()
+37 -6
View File
@@ -27,15 +27,17 @@ type SDK interface {
Data(ctx context.Context, dataset *os.File, filename string, privKey any) error
Result(ctx context.Context, privKey any, resultFile *os.File) error
Attestation(ctx context.Context, reportData [size64]byte, nonce [size32]byte, attType int, attestationFile *os.File) error
IMAMeasurements(ctx context.Context, resultFile *os.File) ([]byte, error)
}
const (
size64 = 64
size32 = 32
algoProgressBarDescription = "Uploading algorithm"
dataProgressBarDescription = "Uploading data"
resultProgressDescription = "Downloading result"
attestationProgressDescription = "Downloading attestation"
size64 = 64
size32 = 32
algoProgressBarDescription = "Uploading algorithm"
dataProgressBarDescription = "Uploading data"
resultProgressDescription = "Downloading result"
attestationProgressDescription = "Downloading attestation"
imaMeasurementsProgressDescription = "Downloading Linux IMA measurements"
)
type agentSDK struct {
@@ -186,3 +188,32 @@ func generateMetadata(userID string, privateKey crypto.PrivateKey) (metadata.MD,
kv[auth.SignatureMetadataKey] = base64.StdEncoding.EncodeToString(signature)
return metadata.New(kv), nil
}
func (sdk *agentSDK) IMAMeasurements(ctx context.Context, resultFile *os.File) ([]byte, error) {
request := &agent.IMAMeasurementsRequest{}
stream, err := sdk.client.IMAMeasurements(ctx, request)
if err != nil {
return nil, err
}
incomingmd, err := stream.Header()
if err != nil {
return nil, err
}
fileSizeStr := incomingmd.Get(grpc.FileSizeKey)
if len(fileSizeStr) == 0 {
fileSizeStr = append(fileSizeStr, "0")
}
fileSize, err := strconv.Atoi(fileSizeStr[0])
if err != nil {
return nil, err
}
pb := progressbar.New(true)
return pb.ReceiveIMAMeasurements(imaMeasurementsProgressDescription, fileSize, stream, resultFile)
}
+59
View File
@@ -173,6 +173,65 @@ func (_c *SDK_Data_Call) RunAndReturn(run func(context.Context, *os.File, string
return _c
}
// IMAMeasurements provides a mock function with given fields: ctx, resultFile
func (_m *SDK) IMAMeasurements(ctx context.Context, resultFile *os.File) ([]byte, error) {
ret := _m.Called(ctx, resultFile)
if len(ret) == 0 {
panic("no return value specified for IMAMeasurements")
}
var r0 []byte
var r1 error
if rf, ok := ret.Get(0).(func(context.Context, *os.File) ([]byte, error)); ok {
return rf(ctx, resultFile)
}
if rf, ok := ret.Get(0).(func(context.Context, *os.File) []byte); ok {
r0 = rf(ctx, resultFile)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).([]byte)
}
}
if rf, ok := ret.Get(1).(func(context.Context, *os.File) error); ok {
r1 = rf(ctx, resultFile)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// SDK_IMAMeasurements_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'IMAMeasurements'
type SDK_IMAMeasurements_Call struct {
*mock.Call
}
// IMAMeasurements is a helper method to define mock.On call
// - ctx context.Context
// - resultFile *os.File
func (_e *SDK_Expecter) IMAMeasurements(ctx interface{}, resultFile interface{}) *SDK_IMAMeasurements_Call {
return &SDK_IMAMeasurements_Call{Call: _e.mock.On("IMAMeasurements", ctx, resultFile)}
}
func (_c *SDK_IMAMeasurements_Call) Run(run func(ctx context.Context, resultFile *os.File)) *SDK_IMAMeasurements_Call {
_c.Call.Run(func(args mock.Arguments) {
run(args[0].(context.Context), args[1].(*os.File))
})
return _c
}
func (_c *SDK_IMAMeasurements_Call) Return(_a0 []byte, _a1 error) *SDK_IMAMeasurements_Call {
_c.Call.Return(_a0, _a1)
return _c
}
func (_c *SDK_IMAMeasurements_Call) RunAndReturn(run func(context.Context, *os.File) ([]byte, error)) *SDK_IMAMeasurements_Call {
_c.Call.Return(run)
return _c
}
// Result provides a mock function with given fields: ctx, privKey, resultFile
func (_m *SDK) Result(ctx context.Context, privKey interface{}, resultFile *os.File) error {
ret := _m.Called(ctx, privKey, resultFile)