package audit import ( "crypto/sha256" "fmt" "sync" "time" ) // ImmutableLogEntry represents a tamper-proof audit entry type ImmutableLogEntry struct { Sequence int64 `json:"sequence"` PrevHash string `json:"prev_hash"` Content string `json:"content"` Hash string `json:"hash"` Timestamp time.Time `json:"timestamp"` Signature string `json:"signature,omitempty"` Metadata map[string]interface{} `json:"metadata,omitempty"` } // ImmutableLog maintains a tamper-proof audit trail type ImmutableLog struct { mu sync.RWMutex entries []*ImmutableLogEntry logPath string sequence int64 prevHash string workflowKey string } // NewImmutableLog creates a new immutable log func NewImmutableLog(logPath string, workflowKey string) *ImmutableLog { return &ImmutableLog{ entries: make([]*ImmutableLogEntry, 0), logPath: logPath, sequence: 0, prevHash: "genesis", workflowKey: workflowKey, } } // Append adds an entry to the immutable log func (il *ImmutableLog) Append(content string, metadata map[string]interface{}) (*ImmutableLogEntry, error) { il.mu.Lock() defer il.mu.Unlock() il.sequence++ hash := il.computeHash(il.sequence, il.prevHash, content) entry := &ImmutableLogEntry{ Sequence: il.sequence, PrevHash: il.prevHash, Content: content, Hash: hash, Timestamp: time.Now(), Metadata: metadata, } il.entries = append(il.entries, entry) il.prevHash = hash return entry, nil } // Verify verifies the integrity of the log func (il *ImmutableLog) Verify() (bool, error) { il.mu.RLock() defer il.mu.RUnlock() prevHash := "genesis" for _, entry := range il.entries { expectedHash := il.computeHash(entry.Sequence, entry.PrevHash, entry.Content) if entry.Hash != expectedHash || entry.PrevHash != prevHash { return false, fmt.Errorf("integrity check failed at sequence %d", entry.Sequence) } prevHash = entry.Hash } return true, nil } // GetEntries returns all entries func (il *ImmutableLog) GetEntries() []*ImmutableLogEntry { il.mu.RLock() defer il.mu.RUnlock() result := make([]*ImmutableLogEntry, len(il.entries)) copy(result, il.entries) return result } // GetLastHash returns the last hash func (il *ImmutableLog) GetLastHash() string { il.mu.RLock() defer il.mu.RUnlock() return il.prevHash } // computeHash computes SHA256 hash func (il *ImmutableLog) computeHash(seq int64, prevHash, content string) string { data := fmt.Sprintf("%d:%s:%s:%s", seq, prevHash, content, il.workflowKey) hash := sha256.Sum256([]byte(data)) return fmt.Sprintf("%x", hash) }